From 363c6a18c43c0bb800cde117a47538ef43b8f024 Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 22 Jul 2020 18:47:26 +0200 Subject: [PATCH 001/320] initial commit --- .gitignore | 1 + 90-backlight.rules | 4 + Cargo.lock | 120 +++++++++++++++++++++++++ Cargo.toml | 16 ++++ README.md | 4 + build.rs | 14 +++ completions/lamp.bash | 73 +++++++++++++++ completions/lamp.fish | 10 +++ src/cli.rs | 82 +++++++++++++++++ src/controllers.rs | 204 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 61 +++++++++++++ 11 files changed, 589 insertions(+) create mode 100644 .gitignore create mode 100644 90-backlight.rules create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 build.rs create mode 100644 completions/lamp.bash create mode 100644 completions/lamp.fish create mode 100644 src/cli.rs create mode 100644 src/controllers.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/90-backlight.rules b/90-backlight.rules new file mode 100644 index 0000000..59ed1f1 --- /dev/null +++ b/90-backlight.rules @@ -0,0 +1,4 @@ +ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness" +ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness" +ACTION=="add", SUBSYSTEM=="leds", RUN+="/bin/chgrp video /sys/class/leds/%k/brightness" +ACTION=="add", SUBSYSTEM=="leds", RUN+="/bin/chmod g+w /sys/class/leds/%k/brightness" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..fa471f0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,120 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" + +[[package]] +name = "hermit-abi" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" +dependencies = [ + "libc", +] + +[[package]] +name = "lamp" +version = "0.1.0" +dependencies = [ + "clap", + "exitcode", +] + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5b39454 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "lamp" +version = "0.1.0" +authors = ["crapStone "] +edition = "2018" + +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +clap = "2.33" + +[dependencies] +clap = "2.33" +exitcode = "1.1.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..8f660aa --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Lamp + +Lamp is a backlight control program written in Rust and inspired by +[acpibacklight](https://gitlab.com/wavexx/acpilight). diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..4365679 --- /dev/null +++ b/build.rs @@ -0,0 +1,14 @@ +use clap::Shell; + +include!("src/cli.rs"); + +fn main() { + let outdir = "completions"; // match env::var_os("OUT_DIR") { + // None => return, + // Some(outdir) => outdir, + // }; + let mut app = build_cli(); + app.gen_completions("lamp", Shell::Fish, outdir); + // app.gen_completions("lamp", Shell::Zsh, outdir); // TODO search for bug + app.gen_completions("lamp", Shell::Bash, outdir); +} diff --git a/completions/lamp.bash b/completions/lamp.bash new file mode 100644 index 0000000..06a16e6 --- /dev/null +++ b/completions/lamp.bash @@ -0,0 +1,73 @@ +_lamp() { + local i cur prev opts cmds + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + cmd="" + opts="" + + for i in ${COMP_WORDS[@]} + do + case "${i}" in + lamp) + cmd="lamp" + ;; + + *) + ;; + esac + done + + case "${cmd}" in + lamp) + opts=" -g -z -f -l -h -V -s -i -d -t --get --zero --full --list --help --version --set --increase --decrease --type " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + + --set) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -s) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --increase) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -i) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --decrease) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -d) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --type) + COMPREPLY=($(compgen -W "raw lin log" -- "${cur}")) + return 0 + ;; + -t) + COMPREPLY=($(compgen -W "raw lin log" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + + esac +} + +complete -F _lamp -o bashdefault -o default lamp diff --git a/completions/lamp.fish b/completions/lamp.fish new file mode 100644 index 0000000..1b0d532 --- /dev/null +++ b/completions/lamp.fish @@ -0,0 +1,10 @@ +complete -c lamp -n "__fish_use_subcommand" -s s -l set -d 'Sets brightness to given value' +complete -c lamp -n "__fish_use_subcommand" -s i -l increase -d 'Increases brightness' +complete -c lamp -n "__fish_use_subcommand" -s d -l decrease -d 'Decreases brightness' +complete -c lamp -n "__fish_use_subcommand" -s t -l type -d 'choose controller type' -r -f -a "raw lin log" +complete -c lamp -n "__fish_use_subcommand" -s g -l get -d 'Prints current brightness value' +complete -c lamp -n "__fish_use_subcommand" -s z -l zero -d 'Sets brightness to lowest value' +complete -c lamp -n "__fish_use_subcommand" -s f -l full -d 'Sets brightness to highest value' +complete -c lamp -n "__fish_use_subcommand" -s l -l list -d 'Lists all available brightness and led controllers' +complete -c lamp -n "__fish_use_subcommand" -s h -l help -d 'Prints help information' +complete -c lamp -n "__fish_use_subcommand" -s V -l version -d 'Prints version information' diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..fd665e2 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,82 @@ +use clap::{App, Arg, ArgGroup, ArgMatches}; + +pub fn build_cli() -> App<'static, 'static> { + App::new("RS Light") + .version("1.0") + .author("crapStone ") + .about("Utility to interact with backlight") + .arg( + Arg::with_name("set") + .short("s") + .long("set") + .value_name("VALUE") + .help("Sets brightness to given value") + .takes_value(true), + ) + .arg( + Arg::with_name("inc") + .short("i") + .long("increase") + .value_name("PERCENT") + .help("Increases brightness") + .takes_value(true), + ) + .arg( + Arg::with_name("dec") + .short("d") + .long("decrease") + .value_name("PERCENT") + .help("Decreases brightness") + .takes_value(true), + ) + .arg( + Arg::with_name("get") + .short("g") + .long("get") + .help("Prints current brightness value"), + ) + .arg( + Arg::with_name("zer") + .short("z") + .long("zero") + .help("Sets brightness to lowest value"), + ) + .arg( + Arg::with_name("ful") + .short("f") + .long("full") + .help("Sets brightness to highest value"), + ) + .group(ArgGroup::with_name("brightness_control").args(&["set", "inc", "dec", "get", "zer", "ful"])) + .arg( + Arg::with_name("list") + .short("l") + .long("list") + .help("Lists all available brightness and led controllers") + .conflicts_with_all(&["brightness_control"]), + ) + .arg( + Arg::with_name("ctrl_type") + .short("t") + .long("type") + .value_name("controller_type") + .takes_value(true) + .possible_values(&["raw", "lin", "log"]) + .default_value("lin") + .help("choose controller type") + .long_help( + r#"You can choose between these controller types: +raw: uses the raw values found in the device files +lin: uses percentage values (0.0 - 1.0) with a linear curve for the actual brightness +log: uses percentage values (0.0 - 1.0) with a logarithmic curve for the actual brightness + the perceived brightness for the human eyes should be linear with this controller +"#, + ), + ) +} + +/// Creates a argument parser with [clap](../clap/index.html) and returns a `Box` with the +/// [matches](../clap/struct.ArgMatches.html). +pub fn parse_args<'a>() -> Box> { + Box::new(build_cli().get_matches()) +} diff --git a/src/controllers.rs b/src/controllers.rs new file mode 100644 index 0000000..79e8342 --- /dev/null +++ b/src/controllers.rs @@ -0,0 +1,204 @@ +use std::collections::HashMap; +use std::fs::{File, OpenOptions}; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; +use std::process::exit; + +use exitcode; + +const SYS_PATHS: [&str; 2] = ["/sys/class/backlight", "/sys/class/leds"]; + +pub trait Controller { + fn get_brightness(&self) -> i32; + fn get_max_brightness(&self) -> i32; + fn set_brightness(&self, value: i32); + + fn check_brightness_value(&self, value: i32) { + if value > self.get_max_brightness() { + eprintln!( + "brightness value too high: {} > {}", + value, + self.get_max_brightness() + ); + exit(exitcode::DATAERR); + } else if value < 0 { + eprintln!("brightness value too low: {}", value); + exit(exitcode::DATAERR); + } + } +} + +pub struct RawController { + path: Box, +} + +impl RawController { + pub fn new(path: Box) -> Self { + Self { path: path } + } +} + +impl Controller for RawController { + fn get_brightness(&self) -> i32 { + read_file_to_int(self.path.join("brightness")) + } + + fn get_max_brightness(&self) -> i32 { + read_file_to_int(self.path.join("max_brightness")) + } + + fn set_brightness(&self, value: i32) { + self.check_brightness_value(value); + + let path = self.path.join("brightness"); + + let mut file = match OpenOptions::new().write(true).read(true).open(&path) { + Err(why) => { + eprintln!("couldn't open '{}': {:?}", &path.display(), why.kind()); + exit(exitcode::OSFILE); + } + Ok(file) => file, + }; + + match write!(file, "{}", value) { + Ok(_) => {} + Err(err) => { + eprintln!( + "could not write '{}' to file '{}': {:?}", + value, + &path.display(), + err.kind() + ); + exit(exitcode::OSFILE); + } + }; + } +} + +pub struct LinController { + parent_controller: RawController, +} + +impl LinController { + pub fn new(path: Box) -> Self { + Self { + parent_controller: RawController::new(path), + } + } +} + +impl Controller for LinController { + fn get_brightness(&self) -> i32 { + ((self.parent_controller.get_brightness() as f64 + / self.parent_controller.get_max_brightness() as f64) + * self.get_max_brightness() as f64) as i32 + } + + fn get_max_brightness(&self) -> i32 { + 100 + } + + fn set_brightness(&self, value: i32) { + self.check_brightness_value(value); + + if value > self.get_max_brightness() { + eprintln!( + "brightness value too high! {} > {}", + value, + self.get_max_brightness() + ); + exit(exitcode::DATAERR); + } + + self.parent_controller.set_brightness( + (value * self.parent_controller.get_max_brightness()) / self.get_max_brightness(), + ) + } +} + +pub struct LogController { + parent_controller: RawController, +} + +impl LogController { + pub fn new(path: Box) -> Self { + Self { + parent_controller: RawController::new(path), + } + } +} + +impl Controller for LogController { + fn get_brightness(&self) -> i32 { + ((self.parent_controller.get_brightness() as f64).log10() + / (self.parent_controller.get_max_brightness() as f64).log10() + * self.get_max_brightness() as f64) as i32 + } + + fn get_max_brightness(&self) -> i32 { + 100 + } + + fn set_brightness(&self, value: i32) { + self.check_brightness_value(value); + + if value > self.get_max_brightness() { + eprintln!( + "brightness value too high! {} > {}", + value, + self.get_max_brightness() + ); + exit(exitcode::DATAERR); + } + + self.parent_controller.set_brightness(10f64.powf( + (value as f64 / self.get_max_brightness() as f64) + * (self.parent_controller.get_max_brightness() as f64).log10(), + ) as i32) + } +} + +fn read_file_to_int(path: PathBuf) -> i32 { + let mut file = match File::open(&path) { + Err(why) => { + eprintln!("couldn't open {}: {:?}", path.display(), why.kind()); + exit(exitcode::OSFILE); + } + Ok(file) => file, + }; + + let mut s = String::new(); + match file.read_to_string(&mut s) { + Err(why) => { + eprintln!("couldn't read {}: {:?}", path.display(), why.kind()); + exit(exitcode::OSFILE); + } + Ok(_) => return s.trim().parse().unwrap(), + } +} + +/// Searches through all paths in `SYS_PATHS` and creates a `HashMap` with the name and absolute path. +/// +/// It returns a `Tuple` of the default backlight name and the `HashMap`. +pub fn get_controllers() -> (String, HashMap>) { + let mut controllers: HashMap> = HashMap::new(); + + let mut default = None; + + for path in SYS_PATHS.iter() { + if Path::new(path).exists() { + for name in Path::new(path).read_dir().unwrap() { + let name = name.unwrap().path(); + let key = String::from(name.file_name().unwrap().to_str().unwrap()); + + if default.is_none() { + default = Some(key.clone()); + } + + controllers.insert(key, Box::new(name)); + } + } + } + + (default.unwrap(), controllers) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a90736b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,61 @@ +mod cli; +mod controllers; + +use std::process::exit; + +use exitcode; + +use controllers::{Controller, LinController, LogController, RawController}; + +fn main() { + let matches = cli::parse_args(); + + let (default_ctrl, ctrls) = controllers::get_controllers(); + + let p = ctrls.get(&default_ctrl).unwrap().to_owned(); + let controller: Box = match matches.value_of("ctrl_type") { + Some("raw") => Box::new(RawController::new(p)), + Some("lin") => Box::new(LinController::new(p)), + Some("log") => Box::new(LogController::new(p)), + Some(_) | None => panic!(ERROR_MSG), + }; + + if matches.is_present("list") { + for ctrl in ctrls.keys() { + println!("{}", ctrl); + } + exit(exitcode::OK); + } else if let Some(value) = matches.value_of("set") { + let new_value = value.parse::().unwrap(); + controller.set_brightness(new_value); + } else if let Some(value) = matches.value_of("inc") { + let new_value = controller.get_brightness() + value.parse::().unwrap(); + controller.set_brightness(new_value.min(controller.get_max_brightness())); + } else if let Some(value) = matches.value_of("dec") { + let new_value = controller.get_brightness() - value.parse::().unwrap(); + controller.set_brightness(new_value.max(0)); + } else if matches.is_present("get") { + println!("{}", controller.get_brightness()); + } else if matches.is_present("zer") { + controller.set_brightness(0); + } else if matches.is_present("ful") { + controller.set_brightness(controller.get_max_brightness()); + } else { + panic!(ERROR_MSG); + } + + exit(exitcode::OK); +} + +// https://xkcd.com/2200/ +const ERROR_MSG: &str = r#" + ERROR! + + If you're seeing this, the code is in what I thought was an unreachable state. + + I could give you advice for what to do. but honestly, why should you trust me? + I clearly screwed this up. I'm writing a message that should never appear, + yet I know it will probably appear someday. + + On a deep level, I know I'm not up to this task. I'm so sorry. +"#; From a6250c7bb2bf5a9ee248b96f5a9c8a630c758cbf Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 22 Jul 2020 18:50:08 +0200 Subject: [PATCH 002/320] removed shell completions --- .gitignore | 1 + completions/lamp.bash | 73 ------------------------------------------- completions/lamp.fish | 10 ------ 3 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 completions/lamp.bash delete mode 100644 completions/lamp.fish diff --git a/.gitignore b/.gitignore index 2f7896d..a5e0b29 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +completions/ diff --git a/completions/lamp.bash b/completions/lamp.bash deleted file mode 100644 index 06a16e6..0000000 --- a/completions/lamp.bash +++ /dev/null @@ -1,73 +0,0 @@ -_lamp() { - local i cur prev opts cmds - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - cmd="" - opts="" - - for i in ${COMP_WORDS[@]} - do - case "${i}" in - lamp) - cmd="lamp" - ;; - - *) - ;; - esac - done - - case "${cmd}" in - lamp) - opts=" -g -z -f -l -h -V -s -i -d -t --get --zero --full --list --help --version --set --increase --decrease --type " - if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - - --set) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -s) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --increase) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -i) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --decrease) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -d) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --type) - COMPREPLY=($(compgen -W "raw lin log" -- "${cur}")) - return 0 - ;; - -t) - COMPREPLY=($(compgen -W "raw lin log" -- "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - - esac -} - -complete -F _lamp -o bashdefault -o default lamp diff --git a/completions/lamp.fish b/completions/lamp.fish deleted file mode 100644 index 1b0d532..0000000 --- a/completions/lamp.fish +++ /dev/null @@ -1,10 +0,0 @@ -complete -c lamp -n "__fish_use_subcommand" -s s -l set -d 'Sets brightness to given value' -complete -c lamp -n "__fish_use_subcommand" -s i -l increase -d 'Increases brightness' -complete -c lamp -n "__fish_use_subcommand" -s d -l decrease -d 'Decreases brightness' -complete -c lamp -n "__fish_use_subcommand" -s t -l type -d 'choose controller type' -r -f -a "raw lin log" -complete -c lamp -n "__fish_use_subcommand" -s g -l get -d 'Prints current brightness value' -complete -c lamp -n "__fish_use_subcommand" -s z -l zero -d 'Sets brightness to lowest value' -complete -c lamp -n "__fish_use_subcommand" -s f -l full -d 'Sets brightness to highest value' -complete -c lamp -n "__fish_use_subcommand" -s l -l list -d 'Lists all available brightness and led controllers' -complete -c lamp -n "__fish_use_subcommand" -s h -l help -d 'Prints help information' -complete -c lamp -n "__fish_use_subcommand" -s V -l version -d 'Prints version information' From 9e9302d050941b4c36eb3f7773cb8f13579f3c6a Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 22 Jul 2020 19:02:30 +0200 Subject: [PATCH 003/320] added MIT license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..86af5a7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 crapStone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 0756993f7eb86d4f771b7b5e95f9e974e80d422c Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Tue, 16 Mar 2021 20:22:06 +0100 Subject: [PATCH 004/320] Implement first version without custom domains, TLS and caching --- .gitignore | 2 + 404.html | 32 +++++ go.mod | 5 + go.sum | 23 ++++ main.go | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 426 insertions(+) create mode 100644 .gitignore create mode 100644 404.html create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1062418 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +*.iml diff --git a/404.html b/404.html new file mode 100644 index 0000000..854781d --- /dev/null +++ b/404.html @@ -0,0 +1,32 @@ + + + + + + Not found + + + + + + + + + +

You found a bug!

+

Sorry, this page doesn't exist or is otherwise inaccessible (code %status)

+ + + Website powered by Codeberg Pages + + + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fd8c9ee --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module codeberg.org/codeberg/pages + +go 1.16 + +require github.com/valyala/fasthttp v1.22.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f00185c --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/klauspost/compress v1.11.8 h1:difgzQsp5mdAz9v8lm3P/I+EpDKMU/6uTMw1y1FObuo= +github.com/klauspost/compress v1.11.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.22.0 h1:OpwH5KDOJ9cS2bq8fD+KfT4IrksK0llvkHf4MZx42jQ= +github.com/valyala/fasthttp v1.22.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210226101413-39120d07d75e/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..3fd546b --- /dev/null +++ b/main.go @@ -0,0 +1,364 @@ +// Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. +// +// Mapping custom domains is not static anymore, but can be done with DNS: +// +// 1) add a "codeberg-pages-domains.txt" text file to your repository, containing the allowed domains +// +// 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to +// "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): +// www.example.org. IN CNAME main.pages.example.codeberg.page. +// 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record +// for "example.org" (if your provider allows ALIAS or similar records): +// example.org IN ALIAS codeberg.page. +// +// Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. +package main + +import ( + "bytes" + "crypto/tls" + "fmt" + "mime" + "net" + "os" + "path" + "strconv" + "strings" + "time" + + _ "embed" + + "github.com/valyala/fasthttp" +) + +// MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static +// pages, or used for comparison in CNAME lookups. Static pages can be accessed through +// https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". +var MainDomainSuffix = []byte(".codeberg.page") + +// GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. +var GiteaRoot = []byte("https://codeberg.org") + +//go:embed 404.html +var NotFoundPage []byte + +// BrokenDNSPage will be shown (with a redirect) when trying to access a domain for which no DNS CNAME record exists. +var BrokenDNSPage = "https://docs.codeberg.org/codeberg-pages/custom-domains/" + +// RawDomain specifies the domain from which raw repository content shall be served in the following format: +// https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} +// (set to []byte(nil) to disable raw content hosting) +var RawDomain = []byte("raw.codeberg.page") + +// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). +var RawInfoPage = "https://docs.codeberg.org/codeberg-pages/raw-content/" + +// AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. +var AllowedCorsDomains = [][]byte{ + RawDomain, + []byte("fonts.codeberg.org"), + []byte("design.codeberg.org"), +} + +// BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. +var BlacklistedPaths = [][]byte{ + []byte("/.well-known/acme-challenge/"), +} + +// IndexPages lists pages that may be considered as index pages for directories. +var IndexPages = []string{ + "index.html", +} + +// ReservedUsernames specifies the usernames that are reserved by Gitea and thus may not be used as owner names. +// The contents are taken from https://github.com/go-gitea/gitea/blob/master/models/user.go#L783; reserved names with +// dots are removed as they are forbidden for Codeberg Pages anyways. +var ReservedUsernames = map[string]struct{}{ + "admin": {}, + "api": {}, + "assets": {}, + "attachments": {}, + "avatars": {}, + "captcha": {}, + "commits": {}, + "debug": {}, + "error": {}, + "explore": {}, + "ghost": {}, + "help": {}, + "install": {}, + "issues": {}, + "less": {}, + "login": {}, + "metrics": {}, + "milestones": {}, + "new": {}, + "notifications": {}, + "org": {}, + "plugins": {}, + "pulls": {}, + "raw": {}, + "repo": {}, + "search": {}, + "stars": {}, + "template": {}, + "user": {}, +} + +// main sets up and starts the web server. +func main() { + // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash + if !bytes.HasPrefix(MainDomainSuffix, []byte{'.'}) { + MainDomainSuffix = append([]byte{'.'}, MainDomainSuffix...) + } + GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) + + // Use HOST and PORT environment variables to determine listening address + address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "80")) + fmt.Printf("Listening on http://%s\n", address) + + // Enable compression by wrapping the handler() method with the compression function provided by FastHTTP + compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) + + // Setup listener and TLS + listener, err := net.Listen("tcp", address) + if err != nil { + fmt.Printf("Couldn't create listener: %s\n", err) + os.Exit(1) + } + if envOr("LETS_ENCRYPT", "0") == "1" { + tls.NewListener(listener, &tls.Config{ + GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + // TODO: check DNS name & get certificate from Let's Encrypt + return nil, fmt.Errorf("NYI") + }, + PreferServerCipherSuites: true, + // TODO: optimize cipher suites, minimum TLS version, etc. + }) + } + + // Start the web server + err = (&fasthttp.Server{ + Handler: compressedHandler, + DisablePreParseMultipartForm: false, + MaxRequestBodySize: 0, + NoDefaultServerHeader: true, + ReadTimeout: 10 * time.Second, + }).Serve(listener) + if err != nil { + fmt.Printf("Couldn't start server: %s\n", err) + os.Exit(1) + } +} + +// handler handles a single HTTP request to the web server. +func handler(ctx *fasthttp.RequestCtx) { + ctx.Response.Header.Set("Server", "Codeberg Pages") + + // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin + ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") + + // Enable caching, but require revalidation to reduce confusion + ctx.Response.Header.Set("Cache-Control", "must-revalidate") + + // Block all methods not required for static pages + if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) + return + } + + // Block blacklisted paths (like ACME challenges) + for _, blacklistedPath := range BlacklistedPaths { + if bytes.HasPrefix(ctx.Path(), blacklistedPath) { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + } + + // Allow CORS for specified domains + if ctx.IsOptions() { + allowCors := false + for _, allowedCorsDomain := range AllowedCorsDomains { + if bytes.Equal(ctx.Request.Host(), allowedCorsDomain) { + allowCors = true + break + } + } + if allowCors { + ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") + } + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) + return + } + + // Prepare request information to Gitea + var targetOwner, targetRepo, targetPath string + var targetOptions = upstreamOptions{ + ForbiddenMimeTypes: map[string]struct{}{}, + TryIndexPages: true, + } + var alsoTryPagesRepo = false // Also try to treat the repo as the first path element & fall back to the "pages" repo + + if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { + // Serve raw content from RawDomain + + targetOptions.TryIndexPages = false + targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} + + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 3) + if len(pathElements) < 3 { + // https://{RawDomain}/{owner}/{repo}/{path} is required + ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) + return + } + targetOwner = pathElements[0] + targetRepo = pathElements[1] + targetPath = pathElements[2] + + } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { + // Serve pages from subdomains of MainDomainSuffix + + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) + targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) + targetRepo = pathElements[0] + targetPath = pathElements[1] + alsoTryPagesRepo = true + + } else { + // Serve pages from external domains + + targetOwner, targetRepo, targetPath = getTargetFromDNS(ctx.Request.Host()) + if targetOwner == "" { + ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) + return + } + } + + // Check if a username can't exist because it's reserved (we'd risk to hit a Gitea route in that case) + if _, ok := ReservedUsernames[targetOwner]; ok { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + + // Pass request to Gitea + url := "/" + targetOwner + "/" + targetRepo + "/raw/" + targetPath + if strings.HasPrefix(targetPath, "blob/") { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + + // Try target + if upstream(ctx, url, targetOptions) { + return + } + + // Try target with pages repo + if alsoTryPagesRepo { + targetPath = targetRepo + "/" + targetPath + targetRepo = "pages" + url := "/" + targetOwner + "/" + targetRepo + "/raw/" + targetPath + if strings.HasPrefix(targetPath, "blob/") { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + + if upstream(ctx, url, targetOptions) { + return + } + } + + returnErrorPage(ctx, fasthttp.StatusNotFound) +} + +func getTargetFromDNS(host []byte) (targetOwner, targetRepo, targetPath string) { + // TODO: read CNAME record for host and "www.{host}" to get those values + // TODO: check codeberg-pages-domains.txt + return +} + +// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced +// with the provided status code. +func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { + ctx.Response.SetStatusCode(code) + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)))) +} + +// upstream requests an URL from GiteaRoot and writes it to the request context; if "final" is set, it also returns a +// 404 error if the page couldn't be loaded. +func upstream(ctx *fasthttp.RequestCtx, url string, options upstreamOptions) (success bool) { + // Prepare necessary (temporary) variables with default values + body := make([]byte, 0) + if options.ForbiddenMimeTypes == nil { + options.ForbiddenMimeTypes = map[string]struct{}{} + } + + // Make a request to the upstream URL + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot) + url, 10 * time.Second) + + // Handle errors + if err != nil { + // Connection error, probably Gitea or the internet connection is down? + fmt.Printf("Couldn't fetch URL \"%s\": %s", url, err) + ctx.Response.SetStatusCode(fasthttp.StatusBadGateway) + return false + } + if status != 200 { + if options.TryIndexPages { + // copy the options struct & try if an index page exists + optionsForIndexPages := options + optionsForIndexPages.TryIndexPages = false + optionsForIndexPages.AppendTrailingSlash = true + for _, indexPage := range IndexPages { + if upstream(ctx, url + "/" + indexPage, optionsForIndexPages) { + return true + } + } + } + ctx.Response.SetStatusCode(status) + return false + } + + // Append trailing slash if missing (for index files) + if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { + ctx.Redirect(string(ctx.Request.URI().Path()) + "/", fasthttp.StatusTemporaryRedirect) + return true + } + + // Set the MIME type + mimeType := mime.TypeByExtension(path.Ext(url)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { + if options.DefaultMimeType != "" { + mimeType = options.DefaultMimeType + } else { + mimeType = "application/octet-stream" + } + } + ctx.Response.Header.SetContentType(mimeType) + + // TODO: enable Caching - set Date header and respect If-Modified-Since! + + // Set the response body + ctx.Response.SetStatusCode(fasthttp.StatusOK) + ctx.Response.SetBody(body) + return true +} + +// upstreamOptions provides various options for the upstream request. +type upstreamOptions struct { + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool + AppendTrailingSlash bool +} + +// envOr reads an environment variable and returns a default value if it's empty. +func envOr(env string, or string) string { + if v := os.Getenv(env); v != "" { + return v + } + return or +} From 1a80684e79fa118233636f86fdba9176799348b6 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 17 Mar 2021 00:24:34 +0100 Subject: [PATCH 005/320] Clean up, fix bugs, implement caching & use API --- go.mod | 5 +- go.sum | 2 + main.go | 224 +++++++++++++++++++++++++++++++++----------------------- 3 files changed, 138 insertions(+), 93 deletions(-) diff --git a/go.mod b/go.mod index fd8c9ee..8f7b835 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module codeberg.org/codeberg/pages go 1.16 -require github.com/valyala/fasthttp v1.22.0 +require ( + github.com/valyala/fasthttp v1.22.0 + github.com/valyala/fastjson v1.6.3 +) diff --git a/go.sum b/go.sum index f00185c..63178eb 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.22.0 h1:OpwH5KDOJ9cS2bq8fD+KfT4IrksK0llvkHf4MZx42jQ= github.com/valyala/fasthttp v1.22.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= +github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= +github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= diff --git a/main.go b/main.go index 3fd546b..b1b5517 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( "fmt" "mime" "net" + "net/url" "os" "path" "strconv" @@ -29,6 +30,7 @@ import ( _ "embed" "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" ) // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static @@ -73,37 +75,10 @@ var IndexPages = []string{ // ReservedUsernames specifies the usernames that are reserved by Gitea and thus may not be used as owner names. // The contents are taken from https://github.com/go-gitea/gitea/blob/master/models/user.go#L783; reserved names with // dots are removed as they are forbidden for Codeberg Pages anyways. -var ReservedUsernames = map[string]struct{}{ - "admin": {}, - "api": {}, - "assets": {}, - "attachments": {}, - "avatars": {}, - "captcha": {}, - "commits": {}, - "debug": {}, - "error": {}, - "explore": {}, - "ghost": {}, - "help": {}, - "install": {}, - "issues": {}, - "less": {}, - "login": {}, - "metrics": {}, - "milestones": {}, - "new": {}, - "notifications": {}, - "org": {}, - "plugins": {}, - "pulls": {}, - "raw": {}, - "repo": {}, - "search": {}, - "stars": {}, - "template": {}, - "user": {}, -} +var ReservedUsernames = createLookupMapFromWords(` + admin api assets attachments avatars captcha commits debug error explore ghost help install issues less login metrics milestones new notifications org plugins pulls raw repo search stars template user + +`) // main sets up and starts the web server. func main() { @@ -139,11 +114,12 @@ func main() { // Start the web server err = (&fasthttp.Server{ - Handler: compressedHandler, + Handler: compressedHandler, DisablePreParseMultipartForm: false, - MaxRequestBodySize: 0, - NoDefaultServerHeader: true, - ReadTimeout: 10 * time.Second, + MaxRequestBodySize: 0, + NoDefaultServerHeader: true, + NoDefaultDate: true, + ReadTimeout: 10 * time.Second, }).Serve(listener) if err != nil { fmt.Printf("Couldn't start server: %s\n", err) @@ -195,28 +171,47 @@ func handler(ctx *fasthttp.RequestCtx) { } // Prepare request information to Gitea - var targetOwner, targetRepo, targetPath string - var targetOptions = upstreamOptions{ + var targetOwner, targetRepo, targetBranch, targetPath string + var targetOptions = &upstreamOptions{ ForbiddenMimeTypes: map[string]struct{}{}, - TryIndexPages: true, + TryIndexPages: true, } - var alsoTryPagesRepo = false // Also try to treat the repo as the first path element & fall back to the "pages" repo if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { // Serve raw content from RawDomain targetOptions.TryIndexPages = false targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} + targetOptions.DefaultMimeType = "text/plain; charset=utf-8" - pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 3) - if len(pathElements) < 3 { - // https://{RawDomain}/{owner}/{repo}/{path} is required + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 4) + if len(pathElements) < 2 { + // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) return } targetOwner = pathElements[0] targetRepo = pathElements[1] - targetPath = pathElements[2] + if len(pathElements) > 3 { + targetPath = strings.Trim(pathElements[2]+"/"+pathElements[3], "/") + } else if len(pathElements) > 2 { + targetPath = pathElements[2] + } + + // raw.codeberg.page/example/myrepo/@main/index.html + if len(pathElements) > 3 && strings.HasPrefix(pathElements[2], "@") { + branch, _ := url.PathUnescape(pathElements[2][1:]) + if branch == "" { + branch = pathElements[2][1:] + } + // Check if the branch exists, otherwise treat it as a file path + targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, branch) + if targetOptions.BranchTimestamp != (time.Time{}) { + targetPath = strings.Trim(pathElements[3], "/") // branch exists, use it + } else { + targetBranch = "" // branch doesn't exist, use default branch + } + } } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix @@ -224,13 +219,21 @@ func handler(ctx *fasthttp.RequestCtx) { pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) targetRepo = pathElements[0] - targetPath = pathElements[1] - alsoTryPagesRepo = true + if len(pathElements) > 1 { + targetPath = strings.Trim(pathElements[1], "/") + } + // Check if the first directory is a repo with a "pages" branch + targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, "pages") + if targetOptions.BranchTimestamp == (time.Time{}) { + targetRepo = "pages" + targetBranch = "" + targetPath = strings.Trim(pathElements[0]+"/"+targetPath, "/") + } } else { // Serve pages from external domains - targetOwner, targetRepo, targetPath = getTargetFromDNS(ctx.Request.Host()) + targetOwner, targetRepo, targetBranch, targetPath = getTargetFromDNS(ctx) if targetOwner == "" { ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) return @@ -243,37 +246,20 @@ func handler(ctx *fasthttp.RequestCtx) { return } - // Pass request to Gitea - url := "/" + targetOwner + "/" + targetRepo + "/raw/" + targetPath + // Check for blob path if strings.HasPrefix(targetPath, "blob/") { returnErrorPage(ctx, fasthttp.StatusForbidden) return } - // Try target - if upstream(ctx, url, targetOptions) { + if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { return } - // Try target with pages repo - if alsoTryPagesRepo { - targetPath = targetRepo + "/" + targetPath - targetRepo = "pages" - url := "/" + targetOwner + "/" + targetRepo + "/raw/" + targetPath - if strings.HasPrefix(targetPath, "blob/") { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - - if upstream(ctx, url, targetOptions) { - return - } - } - - returnErrorPage(ctx, fasthttp.StatusNotFound) + returnErrorPage(ctx, ctx.Response.StatusCode()) } -func getTargetFromDNS(host []byte) (targetOwner, targetRepo, targetPath string) { +func getTargetFromDNS(ctx *fasthttp.RequestCtx) (targetOwner, targetRepo, targetBranch, targetPath string) { // TODO: read CNAME record for host and "www.{host}" to get those values // TODO: check codeberg-pages-domains.txt return @@ -286,49 +272,86 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)))) } +func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, t time.Time) { + branchWithFallback = branch + if branch == "" { + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) + if err != nil || status != 200 { + return + } + branch = fastjson.GetString(body, "default_branch") + branchWithFallback = branch + } + + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) + if err != nil || status != 200 { + return + } + + t, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + return +} + // upstream requests an URL from GiteaRoot and writes it to the request context; if "final" is set, it also returns a // 404 error if the page couldn't be loaded. -func upstream(ctx *fasthttp.RequestCtx, url string, options upstreamOptions) (success bool) { - // Prepare necessary (temporary) variables with default values - body := make([]byte, 0) +func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (success bool) { + fmt.Printf("Trying: %s/%s/%s/%s\n", targetOwner, targetRepo, targetBranch, targetPath) if options.ForbiddenMimeTypes == nil { options.ForbiddenMimeTypes = map[string]struct{}{} } - // Make a request to the upstream URL - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot) + url, 10 * time.Second) - - // Handle errors - if err != nil { - // Connection error, probably Gitea or the internet connection is down? - fmt.Printf("Couldn't fetch URL \"%s\": %s", url, err) - ctx.Response.SetStatusCode(fasthttp.StatusBadGateway) - return false + // Check if the branch exists and when it was modified + if options.BranchTimestamp == (time.Time{}) { + targetBranch, options.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, targetBranch) + if options.BranchTimestamp == (time.Time{}) { + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + return false + } } - if status != 200 { + + if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { + if !ifModifiedSince.Before(options.BranchTimestamp) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + return true + } + } + + // Make a GET request to the upstream URL + req := fasthttp.AcquireRequest() + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) + res := fasthttp.AcquireResponse() + err := fasthttp.DoTimeout(req, res, 10*time.Second) + if res.StatusCode() == fasthttp.StatusNotFound { if options.TryIndexPages { // copy the options struct & try if an index page exists - optionsForIndexPages := options + optionsForIndexPages := *options optionsForIndexPages.TryIndexPages = false optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range IndexPages { - if upstream(ctx, url + "/" + indexPage, optionsForIndexPages) { + if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { return true } } } - ctx.Response.SetStatusCode(status) + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) return false } + if err != nil || res.StatusCode() != fasthttp.StatusOK { + fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } // Append trailing slash if missing (for index files) if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { - ctx.Redirect(string(ctx.Request.URI().Path()) + "/", fasthttp.StatusTemporaryRedirect) + ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) return true } // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(url)) + mimeType := mime.TypeByExtension(path.Ext(targetPath)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { if options.DefaultMimeType != "" { @@ -339,20 +362,26 @@ func upstream(ctx *fasthttp.RequestCtx, url string, options upstreamOptions) (su } ctx.Response.Header.SetContentType(mimeType) - // TODO: enable Caching - set Date header and respect If-Modified-Since! - - // Set the response body + // Write the response to the original request ctx.Response.SetStatusCode(fasthttp.StatusOK) - ctx.Response.SetBody(body) + ctx.Response.Header.SetLastModified(options.BranchTimestamp) + err = res.BodyWriteTo(ctx.Response.BodyWriter()) + if err != nil { + fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + return true } // upstreamOptions provides various options for the upstream request. type upstreamOptions struct { - DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} - TryIndexPages bool + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool AppendTrailingSlash bool + BranchTimestamp time.Time } // envOr reads an environment variable and returns a default value if it's empty. @@ -362,3 +391,14 @@ func envOr(env string, or string) string { } return or } + +func createLookupMapFromWords(input string) map[string]struct{} { + var res = map[string]struct{}{} + input = strings.NewReplacer("\t", " ", "\n", " ", "\r", " ").Replace(input) + for _, word := range strings.Split(input, " ") { + if len(word) > 0 { + res[word] = struct{}{} + } + } + return res +} From 241f7a57ec247260b01b913b804eff3126468b1c Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 17 Mar 2021 00:34:31 +0100 Subject: [PATCH 006/320] Split code into multiple files --- certificates.go | 16 +++ domains.go | 11 ++ handler.go | 269 +++++++++++++++++++++++++++++++++++++++++++++++ main.go | 273 +----------------------------------------------- 4 files changed, 298 insertions(+), 271 deletions(-) create mode 100644 certificates.go create mode 100644 domains.go create mode 100644 handler.go diff --git a/certificates.go b/certificates.go new file mode 100644 index 0000000..78e1110 --- /dev/null +++ b/certificates.go @@ -0,0 +1,16 @@ +package main + +import ( + "crypto/tls" + "fmt" +) + +// tlsConfig contains the configuration for generating, serving and cleaning up Let's Encrypt certificates. +var tlsConfig = &tls.Config{ + GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + // TODO: check DNS name & get certificate from Let's Encrypt + return nil, fmt.Errorf("NYI") + }, + PreferServerCipherSuites: true, + // TODO: optimize cipher suites, minimum TLS version, etc. +} diff --git a/domains.go b/domains.go new file mode 100644 index 0000000..d0b25b1 --- /dev/null +++ b/domains.go @@ -0,0 +1,11 @@ +package main + +import "github.com/valyala/fasthttp" + +// getTargetFromDNS searches for CNAME entries on the request domain, optionally with a "www." prefix, and checks if +// the domain is included in the repository's "domains.txt" file. If everything is fine, it returns the target data. +func getTargetFromDNS(ctx *fasthttp.RequestCtx) (targetOwner, targetRepo, targetBranch, targetPath string) { + // TODO: read CNAME record for host and "www.{host}" to get those values + // TODO: check domains.txt + return +} diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..02a1a73 --- /dev/null +++ b/handler.go @@ -0,0 +1,269 @@ +package main + +import ( + "bytes" + "fmt" + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" + "mime" + "net/url" + "path" + "strconv" + "strings" + "time" +) + +// handler handles a single HTTP request to the web server. +func handler(ctx *fasthttp.RequestCtx) { + ctx.Response.Header.Set("Server", "Codeberg Pages") + + // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin + ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") + + // Enable caching, but require revalidation to reduce confusion + ctx.Response.Header.Set("Cache-Control", "must-revalidate") + + // Block all methods not required for static pages + if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) + return + } + + // Block blacklisted paths (like ACME challenges) + for _, blacklistedPath := range BlacklistedPaths { + if bytes.HasPrefix(ctx.Path(), blacklistedPath) { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + } + + // Allow CORS for specified domains + if ctx.IsOptions() { + allowCors := false + for _, allowedCorsDomain := range AllowedCorsDomains { + if bytes.Equal(ctx.Request.Host(), allowedCorsDomain) { + allowCors = true + break + } + } + if allowCors { + ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") + } + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) + return + } + + // Prepare request information to Gitea + var targetOwner, targetRepo, targetBranch, targetPath string + var targetOptions = &upstreamOptions{ + ForbiddenMimeTypes: map[string]struct{}{}, + TryIndexPages: true, + } + + if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { + // Serve raw content from RawDomain + + targetOptions.TryIndexPages = false + targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} + targetOptions.DefaultMimeType = "text/plain; charset=utf-8" + + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 4) + if len(pathElements) < 2 { + // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required + ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) + return + } + targetOwner = pathElements[0] + targetRepo = pathElements[1] + if len(pathElements) > 3 { + targetPath = strings.Trim(pathElements[2]+"/"+pathElements[3], "/") + } else if len(pathElements) > 2 { + targetPath = pathElements[2] + } + + // raw.codeberg.page/example/myrepo/@main/index.html + if len(pathElements) > 3 && strings.HasPrefix(pathElements[2], "@") { + branch, _ := url.PathUnescape(pathElements[2][1:]) + if branch == "" { + branch = pathElements[2][1:] + } + // Check if the branch exists, otherwise treat it as a file path + targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, branch) + if targetOptions.BranchTimestamp != (time.Time{}) { + targetPath = strings.Trim(pathElements[3], "/") // branch exists, use it + } else { + targetBranch = "" // branch doesn't exist, use default branch + } + } + + } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { + // Serve pages from subdomains of MainDomainSuffix + + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) + targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) + targetRepo = pathElements[0] + if len(pathElements) > 1 { + targetPath = strings.Trim(pathElements[1], "/") + } + + // Check if the first directory is a repo with a "pages" branch + targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, "pages") + if targetOptions.BranchTimestamp == (time.Time{}) { + targetRepo = "pages" + targetBranch = "" + targetPath = strings.Trim(pathElements[0]+"/"+targetPath, "/") + } + } else { + // Serve pages from external domains + + targetOwner, targetRepo, targetBranch, targetPath = getTargetFromDNS(ctx) + if targetOwner == "" { + ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) + return + } + } + + // Check if a username can't exist because it's reserved (we'd risk to hit a Gitea route in that case) + if _, ok := ReservedUsernames[targetOwner]; ok { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + + // Check for blob path + if strings.HasPrefix(targetPath, "blob/") { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + + // Try to request the file from the Gitea API + if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { + returnErrorPage(ctx, ctx.Response.StatusCode()) + } +} + +// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced +// with the provided status code. +func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { + ctx.Response.SetStatusCode(code) + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)))) +} + +// getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch +// (or an empty time.Time if the branch doesn't exist) +func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, t time.Time) { + branchWithFallback = branch + if branch == "" { + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) + if err != nil || status != 200 { + branchWithFallback = "" + return + } + branch = fastjson.GetString(body, "default_branch") + branchWithFallback = branch + } + + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) + if err != nil || status != 200 { + branchWithFallback = "" + return + } + + t, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + return +} + +// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. +func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (success bool) { + if options.ForbiddenMimeTypes == nil { + options.ForbiddenMimeTypes = map[string]struct{}{} + } + + // Check if the branch exists and when it was modified + if options.BranchTimestamp == (time.Time{}) { + targetBranch, options.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, targetBranch) + if options.BranchTimestamp == (time.Time{}) { + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + return false + } + } + + // Check if the browser has a cached version + if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { + if !ifModifiedSince.Before(options.BranchTimestamp) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + return true + } + } + + // Make a GET request to the upstream URL + req := fasthttp.AcquireRequest() + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) + res := fasthttp.AcquireResponse() + err := fasthttp.DoTimeout(req, res, 10*time.Second) + + // Handle errors + if res.StatusCode() == fasthttp.StatusNotFound { + if options.TryIndexPages { + // copy the options struct & try if an index page exists + optionsForIndexPages := *options + optionsForIndexPages.TryIndexPages = false + optionsForIndexPages.AppendTrailingSlash = true + for _, indexPage := range IndexPages { + if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { + return true + } + } + } + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + return false + } + if err != nil || res.StatusCode() != fasthttp.StatusOK { + fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + + // Append trailing slash if missing (for index files) + if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { + ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) + return true + } + + // Set the MIME type + mimeType := mime.TypeByExtension(path.Ext(targetPath)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { + if options.DefaultMimeType != "" { + mimeType = options.DefaultMimeType + } else { + mimeType = "application/octet-stream" + } + } + ctx.Response.Header.SetContentType(mimeType) + + // Write the response to the original request + ctx.Response.SetStatusCode(fasthttp.StatusOK) + ctx.Response.Header.SetLastModified(options.BranchTimestamp) + err = res.BodyWriteTo(ctx.Response.BodyWriter()) + if err != nil { + fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + + return true +} + +// upstreamOptions provides various options for the upstream request. +type upstreamOptions struct { + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool + AppendTrailingSlash bool + BranchTimestamp time.Time +} diff --git a/main.go b/main.go index b1b5517..ae4ac7b 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,7 @@ // // Mapping custom domains is not static anymore, but can be done with DNS: // -// 1) add a "codeberg-pages-domains.txt" text file to your repository, containing the allowed domains +// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. // // 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to // "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): @@ -18,19 +18,14 @@ import ( "bytes" "crypto/tls" "fmt" - "mime" "net" - "net/url" "os" - "path" - "strconv" "strings" "time" _ "embed" "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" ) // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static @@ -102,14 +97,7 @@ func main() { os.Exit(1) } if envOr("LETS_ENCRYPT", "0") == "1" { - tls.NewListener(listener, &tls.Config{ - GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - // TODO: check DNS name & get certificate from Let's Encrypt - return nil, fmt.Errorf("NYI") - }, - PreferServerCipherSuites: true, - // TODO: optimize cipher suites, minimum TLS version, etc. - }) + tls.NewListener(listener, tlsConfig) } // Start the web server @@ -127,263 +115,6 @@ func main() { } } -// handler handles a single HTTP request to the web server. -func handler(ctx *fasthttp.RequestCtx) { - ctx.Response.Header.Set("Server", "Codeberg Pages") - - // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin - ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") - - // Enable caching, but require revalidation to reduce confusion - ctx.Response.Header.Set("Cache-Control", "must-revalidate") - - // Block all methods not required for static pages - if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) - return - } - - // Block blacklisted paths (like ACME challenges) - for _, blacklistedPath := range BlacklistedPaths { - if bytes.HasPrefix(ctx.Path(), blacklistedPath) { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - } - - // Allow CORS for specified domains - if ctx.IsOptions() { - allowCors := false - for _, allowedCorsDomain := range AllowedCorsDomains { - if bytes.Equal(ctx.Request.Host(), allowedCorsDomain) { - allowCors = true - break - } - } - if allowCors { - ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") - ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") - } - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) - return - } - - // Prepare request information to Gitea - var targetOwner, targetRepo, targetBranch, targetPath string - var targetOptions = &upstreamOptions{ - ForbiddenMimeTypes: map[string]struct{}{}, - TryIndexPages: true, - } - - if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { - // Serve raw content from RawDomain - - targetOptions.TryIndexPages = false - targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} - targetOptions.DefaultMimeType = "text/plain; charset=utf-8" - - pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 4) - if len(pathElements) < 2 { - // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required - ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) - return - } - targetOwner = pathElements[0] - targetRepo = pathElements[1] - if len(pathElements) > 3 { - targetPath = strings.Trim(pathElements[2]+"/"+pathElements[3], "/") - } else if len(pathElements) > 2 { - targetPath = pathElements[2] - } - - // raw.codeberg.page/example/myrepo/@main/index.html - if len(pathElements) > 3 && strings.HasPrefix(pathElements[2], "@") { - branch, _ := url.PathUnescape(pathElements[2][1:]) - if branch == "" { - branch = pathElements[2][1:] - } - // Check if the branch exists, otherwise treat it as a file path - targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, branch) - if targetOptions.BranchTimestamp != (time.Time{}) { - targetPath = strings.Trim(pathElements[3], "/") // branch exists, use it - } else { - targetBranch = "" // branch doesn't exist, use default branch - } - } - - } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { - // Serve pages from subdomains of MainDomainSuffix - - pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) - targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) - targetRepo = pathElements[0] - if len(pathElements) > 1 { - targetPath = strings.Trim(pathElements[1], "/") - } - - // Check if the first directory is a repo with a "pages" branch - targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, "pages") - if targetOptions.BranchTimestamp == (time.Time{}) { - targetRepo = "pages" - targetBranch = "" - targetPath = strings.Trim(pathElements[0]+"/"+targetPath, "/") - } - } else { - // Serve pages from external domains - - targetOwner, targetRepo, targetBranch, targetPath = getTargetFromDNS(ctx) - if targetOwner == "" { - ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) - return - } - } - - // Check if a username can't exist because it's reserved (we'd risk to hit a Gitea route in that case) - if _, ok := ReservedUsernames[targetOwner]; ok { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - - // Check for blob path - if strings.HasPrefix(targetPath, "blob/") { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - - if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { - return - } - - returnErrorPage(ctx, ctx.Response.StatusCode()) -} - -func getTargetFromDNS(ctx *fasthttp.RequestCtx) (targetOwner, targetRepo, targetBranch, targetPath string) { - // TODO: read CNAME record for host and "www.{host}" to get those values - // TODO: check codeberg-pages-domains.txt - return -} - -// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced -// with the provided status code. -func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { - ctx.Response.SetStatusCode(code) - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)))) -} - -func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, t time.Time) { - branchWithFallback = branch - if branch == "" { - var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) - if err != nil || status != 200 { - return - } - branch = fastjson.GetString(body, "default_branch") - branchWithFallback = branch - } - - var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) - if err != nil || status != 200 { - return - } - - t, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - return -} - -// upstream requests an URL from GiteaRoot and writes it to the request context; if "final" is set, it also returns a -// 404 error if the page couldn't be loaded. -func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (success bool) { - fmt.Printf("Trying: %s/%s/%s/%s\n", targetOwner, targetRepo, targetBranch, targetPath) - if options.ForbiddenMimeTypes == nil { - options.ForbiddenMimeTypes = map[string]struct{}{} - } - - // Check if the branch exists and when it was modified - if options.BranchTimestamp == (time.Time{}) { - targetBranch, options.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, targetBranch) - if options.BranchTimestamp == (time.Time{}) { - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - return false - } - } - - if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { - if !ifModifiedSince.Before(options.BranchTimestamp) { - ctx.Response.SetStatusCode(fasthttp.StatusNotModified) - return true - } - } - - // Make a GET request to the upstream URL - req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) - res := fasthttp.AcquireResponse() - err := fasthttp.DoTimeout(req, res, 10*time.Second) - if res.StatusCode() == fasthttp.StatusNotFound { - if options.TryIndexPages { - // copy the options struct & try if an index page exists - optionsForIndexPages := *options - optionsForIndexPages.TryIndexPages = false - optionsForIndexPages.AppendTrailingSlash = true - for _, indexPage := range IndexPages { - if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { - return true - } - } - } - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - return false - } - if err != nil || res.StatusCode() != fasthttp.StatusOK { - fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - - // Append trailing slash if missing (for index files) - if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { - ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) - return true - } - - // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(targetPath)) - mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { - if options.DefaultMimeType != "" { - mimeType = options.DefaultMimeType - } else { - mimeType = "application/octet-stream" - } - } - ctx.Response.Header.SetContentType(mimeType) - - // Write the response to the original request - ctx.Response.SetStatusCode(fasthttp.StatusOK) - ctx.Response.Header.SetLastModified(options.BranchTimestamp) - err = res.BodyWriteTo(ctx.Response.BodyWriter()) - if err != nil { - fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - - return true -} - -// upstreamOptions provides various options for the upstream request. -type upstreamOptions struct { - DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} - TryIndexPages bool - AppendTrailingSlash bool - BranchTimestamp time.Time -} - // envOr reads an environment variable and returns a default value if it's empty. func envOr(env string, or string) string { if v := os.Getenv(env); v != "" { From cdd67270492043818411d70c697f7b8c48788b22 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 17 Mar 2021 01:16:57 +0100 Subject: [PATCH 007/320] Fix error page not rendering & make it more beautiful --- 404.html | 22 +++++++++++++--------- certificates.go | 2 ++ handler.go | 15 ++++++++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/404.html b/404.html index 854781d..3b5265c 100644 --- a/404.html +++ b/404.html @@ -3,16 +3,16 @@ - Not found + %status - + - -

You found a bug!

-

Sorry, this page doesn't exist or is otherwise inaccessible (code %status)

- - - Website powered by Codeberg Pages + +

+ You found a bug! +

+
+ Sorry, this page doesn't exist or is inaccessible for other reasons (%status) +
+ + + Static pages made easy - Codeberg Pages diff --git a/certificates.go b/certificates.go index 78e1110..680fea8 100644 --- a/certificates.go +++ b/certificates.go @@ -14,3 +14,5 @@ var tlsConfig = &tls.Config{ PreferServerCipherSuites: true, // TODO: optimize cipher suites, minimum TLS version, etc. } + +// TODO: HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain diff --git a/handler.go b/handler.go index 02a1a73..97c38ad 100644 --- a/handler.go +++ b/handler.go @@ -148,7 +148,8 @@ func handler(ctx *fasthttp.RequestCtx) { // with the provided status code. func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetStatusCode(code) - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)))) + ctx.Response.Header.SetContentType("text/html; charset=utf-8") + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code) + " " + fasthttp.StatusMessage(code)))) } // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch @@ -186,10 +187,14 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // Check if the branch exists and when it was modified if options.BranchTimestamp == (time.Time{}) { targetBranch, options.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, targetBranch) - if options.BranchTimestamp == (time.Time{}) { - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - return false - } + } + + // Handle repositories with no/broken pages setup + if options.BranchTimestamp == (time.Time{}) || targetBranch == "" { + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + ctx.Response.Header.SetContentType("text/html; charset=utf-8") + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte("pages not set up for this repo"))) + return true } // Check if the browser has a cached version From 44c41c74590a640ef5baa49e79e5be06764173d5 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 17 Mar 2021 01:21:50 +0100 Subject: [PATCH 008/320] Release under EUPL v. 1.2 --- LICENSE | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c108cad --- /dev/null +++ b/LICENSE @@ -0,0 +1,305 @@ +European Union Public Licence v. 1.2 + +EUPL © the European Union 2007, 2016 + +This European Union Public Licence (the 'EUPL') applies to the Work (as defined +below) which is provided under the terms of this Licence. Any use of the Work, +other than as authorised under this Licence is prohibited (to the extent such +use is covered by a right of the copyright holder of the Work). + +The Work is provided under the terms of this Licence when the Licensor (as +defined below) has placed the following notice immediately following the copyright +notice for the Work: + + + + Licensed under the EUPL + + + +or has expressed by any other means his willingness to license under the EUPL. + + 1. Definitions + + In this Licence, the following terms have the following meaning: + + — 'The Licence': this Licence. + +— 'The Original Work': the work or software distributed or communicated by +the Licensor under this Licence, available as Source Code and also as Executable +Code as the case may be. + +— 'Derivative Works': the works or software that could be created by the Licensee, +based upon the Original Work or modifications thereof. This Licence does not +define the extent of modification or dependence on the Original Work required +in order to classify a work as a Derivative Work; this extent is determined +by copyright law applicable in the country mentioned in Article 15. + + — 'The Work': the Original Work or its Derivative Works. + +— 'The Source Code': the human-readable form of the Work which is the most +convenient for people to study and modify. + +— 'The Executable Code': any code which has generally been compiled and which +is meant to be interpreted by a computer as a program. + +— 'The Licensor': the natural or legal person that distributes or communicates +the Work under the Licence. + +— 'Contributor(s)': any natural or legal person who modifies the Work under +the Licence, or otherwise contributes to the creation of a Derivative Work. + +— 'The Licensee' or 'You': any natural or legal person who makes any usage +of the Work under the terms of the Licence. + +— 'Distribution' or 'Communication': any act of selling, giving, lending, +renting, distributing, communicating, transmitting, or otherwise making available, +online or offline, copies of the Work or providing access to its essential +functionalities at the disposal of any other natural or legal person. + + 2. Scope of the rights granted by the Licence + +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable +licence to do the following, for the duration of copyright vested in the Original +Work: + + — use the Work in any circumstance and for all usage, + + — reproduce the Work, + + — modify the Work, and make Derivative Works based upon the Work, + +— communicate to the public, including the right to make available or display +the Work or copies thereof to the public and perform publicly, as the case +may be, the Work, + + — distribute the Work or copies thereof, + + — lend and rent the Work or copies thereof, + + — sublicense rights in the Work or copies thereof. + +Those rights can be exercised on any media, supports and formats, whether +now known or later invented, as far as the applicable law permits so. + +In the countries where moral rights apply, the Licensor waives his right to +exercise his moral right to the extent allowed by law in order to make effective +the licence of the economic rights here above listed. + +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights +to any patents held by the Licensor, to the extent necessary to make use of +the rights granted on the Work under this Licence. + + 3. Communication of the Source Code + +The Licensor may provide the Work either in its Source Code form, or as Executable +Code. If the Work is provided as Executable Code, the Licensor provides in +addition a machine-readable copy of the Source Code of the Work along with +each copy of the Work that the Licensor distributes or indicates, in a notice +following the copyright notice attached to the Work, a repository where the +Source Code is easily and freely accessible for as long as the Licensor continues +to distribute or communicate the Work. + + 4. Limitations on copyright + +Nothing in this Licence is intended to deprive the Licensee of the benefits +from any exception or limitation to the exclusive rights of the rights owners +in the Work, of the exhaustion of those rights or of other applicable limitations +thereto. + + 5. Obligations of the Licensee + +The grant of the rights mentioned above is subject to some restrictions and +obligations imposed on the Licensee. Those obligations are the following: + +Attribution right: The Licensee shall keep intact all copyright, patent or +trademarks notices and all notices that refer to the Licence and to the disclaimer +of warranties. The Licensee must include a copy of such notices and a copy +of the Licence with every copy of the Work he/she distributes or communicates. +The Licensee must cause any Derivative Work to carry prominent notices stating +that the Work has been modified and the date of modification. + +Copyleft clause: If the Licensee distributes or communicates copies of the +Original Works or Derivative Works, this Distribution or Communication will +be done under the terms of this Licence or of a later version of this Licence +unless the Original Work is expressly distributed only under this version +of the Licence — for example by communicating 'EUPL v. 1.2 only'. The Licensee +(becoming Licensor) cannot offer or impose any additional terms or conditions +on the Work or Derivative Work that alter or restrict the terms of the Licence. + +Compatibility clause: If the Licensee Distributes or Communicates Derivative +Works or copies thereof based upon both the Work and another work licensed +under a Compatible Licence, this Distribution or Communication can be done +under the terms of this Compatible Licence. For the sake of this clause, 'Compatible +Licence' refers to the licences listed in the appendix attached to this Licence. +Should the Licensee's obligations under the Compatible Licence conflict with +his/her obligations under this Licence, the obligations of the Compatible +Licence shall prevail. + +Provision of Source Code: When distributing or communicating copies of the +Work, the Licensee will provide a machine-readable copy of the Source Code +or indicate a repository where this Source will be easily and freely available +for as long as the Licensee continues to distribute or communicate the Work. + +Legal Protection: This Licence does not grant permission to use the trade +names, trademarks, service marks, or names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the copyright notice. + + 6. Chain of Authorship + +The original Licensor warrants that the copyright in the Original Work granted +hereunder is owned by him/her or licensed to him/her and that he/she has the +power and authority to grant the Licence. + +Each Contributor warrants that the copyright in the modifications he/she brings +to the Work are owned by him/her or licensed to him/her and that he/she has +the power and authority to grant the Licence. + +Each time You accept the Licence, the original Licensor and subsequent Contributors +grant You a licence to their contributions to the Work, under the terms of +this Licence. + + 7. Disclaimer of Warranty + +The Work is a work in progress, which is continuously improved by numerous +Contributors. It is not a finished work and may therefore contain defects +or 'bugs' inherent to this type of development. + +For the above reason, the Work is provided under the Licence on an 'as is' +basis and without warranties of any kind concerning the Work, including without +limitation merchantability, fitness for a particular purpose, absence of defects +or errors, accuracy, non-infringement of intellectual property rights other +than copyright as stated in Article 6 of this Licence. + +This disclaimer of warranty is an essential part of the Licence and a condition +for the grant of any rights to the Work. + + 8. Disclaimer of Liability + +Except in the cases of wilful misconduct or damages directly caused to natural +persons, the Licensor will in no event be liable for any direct or indirect, +material or moral, damages of any kind, arising out of the Licence or of the +use of the Work, including without limitation, damages for loss of goodwill, +work stoppage, computer failure or malfunction, loss of data or any commercial +damage, even if the Licensor has been advised of the possibility of such damage. +However, the Licensor will be liable under statutory product liability laws +as far such laws apply to the Work. + + 9. Additional agreements + +While distributing the Work, You may choose to conclude an additional agreement, +defining obligations or services consistent with this Licence. However, if +accepting obligations, You may act only on your own behalf and on your sole +responsibility, not on behalf of the original Licensor or any other Contributor, +and only if You agree to indemnify, defend, and hold each Contributor harmless +for any liability incurred by, or claims asserted against such Contributor +by the fact You have accepted any warranty or additional liability. + + 10. Acceptance of the Licence + +The provisions of this Licence can be accepted by clicking on an icon 'I agree' +placed under the bottom of a window displaying the text of this Licence or +by affirming consent in any other similar way, in accordance with the rules +of applicable law. Clicking on that icon indicates your clear and irrevocable +acceptance of this Licence and all of its terms and conditions. + +Similarly, you irrevocably accept this Licence and all of its terms and conditions +by exercising any rights granted to You by Article 2 of this Licence, such +as the use of the Work, the creation by You of a Derivative Work or the Distribution +or Communication by You of the Work or copies thereof. + + 11. Information to the public + +In case of any Distribution or Communication of the Work by means of electronic +communication by You (for example, by offering to download the Work from a +remote location) the distribution channel or media (for example, a website) +must at least provide to the public the information requested by the applicable +law regarding the Licensor, the Licence and the way it may be accessible, +concluded, stored and reproduced by the Licensee. + + 12. Termination of the Licence + +The Licence and the rights granted hereunder will terminate automatically +upon any breach by the Licensee of the terms of the Licence. + +Such a termination will not terminate the licences of any person who has received +the Work from the Licensee under the Licence, provided such persons remain +in full compliance with the Licence. + + 13. Miscellaneous + +Without prejudice of Article 9 above, the Licence represents the complete +agreement between the Parties as to the Work. + +If any provision of the Licence is invalid or unenforceable under applicable +law, this will not affect the validity or enforceability of the Licence as +a whole. Such provision will be construed or reformed so as necessary to make +it valid and enforceable. + +The European Commission may publish other linguistic versions or new versions +of this Licence or updated versions of the Appendix, so far this is required +and reasonable, without reducing the scope of the rights granted by the Licence. +New versions of the Licence will be published with a unique version number. + +All linguistic versions of this Licence, approved by the European Commission, +have identical value. Parties can take advantage of the linguistic version +of their choice. + + 14. Jurisdiction + + Without prejudice to specific agreement between parties, + +— any litigation resulting from the interpretation of this License, arising +between the European Union institutions, bodies, offices or agencies, as a +Licensor, and any Licensee, will be subject to the jurisdiction of the Court +of Justice of the European Union, as laid down in article 272 of the Treaty +on the Functioning of the European Union, + +— any litigation arising between other parties and resulting from the interpretation +of this License, will be subject to the exclusive jurisdiction of the competent +court where the Licensor resides or conducts its primary business. + + 15. Applicable Law + + Without prejudice to specific agreement between parties, + +— this Licence shall be governed by the law of the European Union Member State +where the Licensor has his seat, resides or has his registered office, + +— this licence shall be governed by Belgian law if the Licensor has no seat, +residence or registered office inside a European Union Member State. + +Appendix + +'Compatible Licences' according to Article 5 EUPL are: + + — GNU General Public License (GPL) v. 2, v. 3 + + — GNU Affero General Public License (AGPL) v. 3 + + — Open Software License (OSL) v. 2.1, v. 3.0 + + — Eclipse Public License (EPL) v. 1.0 + + — CeCILL v. 2.0, v. 2.1 + + — Mozilla Public Licence (MPL) v. 2 + + — GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 + +— Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for +works other than software + + — European Union Public Licence (EUPL) v. 1.1, v. 1.2 + +— Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong Reciprocity +(LiLiQ-R+). + +The European Commission may update this Appendix to later versions of the +above licences without producing a new version of the EUPL, as long as they +provide the rights granted in Article 2 of this Licence and protect the covered +Source Code from exclusive appropriation. + +All other changes or additions to this Appendix require the production of +a new EUPL version. From c4bae34b713182bfaa3970cd5160a84285684055 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 19 Mar 2021 13:20:00 +0100 Subject: [PATCH 009/320] Add TODOs --- domains.go | 4 ++++ handler.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/domains.go b/domains.go index d0b25b1..c244e40 100644 --- a/domains.go +++ b/domains.go @@ -4,8 +4,12 @@ import "github.com/valyala/fasthttp" // getTargetFromDNS searches for CNAME entries on the request domain, optionally with a "www." prefix, and checks if // the domain is included in the repository's "domains.txt" file. If everything is fine, it returns the target data. +// TODO: use TXT records with A/AAAA/ALIAS func getTargetFromDNS(ctx *fasthttp.RequestCtx) (targetOwner, targetRepo, targetBranch, targetPath string) { // TODO: read CNAME record for host and "www.{host}" to get those values // TODO: check domains.txt return } + +// TODO: cache domains.txt for 15 minutes +// TODO: canonical domains - redirect to first domain if domains.txt exists, also make sure owner.codeberg.page/pages/... redirects to /... diff --git a/handler.go b/handler.go index 97c38ad..be78499 100644 --- a/handler.go +++ b/handler.go @@ -66,6 +66,8 @@ func handler(ctx *fasthttp.RequestCtx) { if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { // Serve raw content from RawDomain + // TODO: add canonical link and "X-Robots-Tag: noarchive, noindex" + targetOptions.TryIndexPages = false targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} targetOptions.DefaultMimeType = "text/plain; charset=utf-8" @@ -102,6 +104,8 @@ func handler(ctx *fasthttp.RequestCtx) { } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix + // TODO: add @branch syntax with "X-Robots-Tag: noarchive, noindex" + pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) targetRepo = pathElements[0] @@ -154,6 +158,7 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or an empty time.Time if the branch doesn't exist) +// TODO: cache responses for ~15 minutes if a branch exists func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, t time.Time) { branchWithFallback = branch if branch == "" { From 8ead10c82e88b8f598fcb9b71cfae6fbe7978260 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 19 Mar 2021 20:30:08 +0100 Subject: [PATCH 010/320] Implement SEO optimizations and improve error handling and branch detection --- handler.go | 162 ++++++++++++++++++++++++++++++++++++----------------- main.go | 20 ------- 2 files changed, 111 insertions(+), 71 deletions(-) diff --git a/handler.go b/handler.go index be78499..adf5c25 100644 --- a/handler.go +++ b/handler.go @@ -63,16 +63,58 @@ func handler(ctx *fasthttp.RequestCtx) { TryIndexPages: true, } + // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will + // also disallow search indexing and add a Link header to the canonical URL. + var tryBranch = func(repo string, branch string, path []string, canonicalLink string) bool { + if repo == "" { + return false + } + fmt.Printf("Trying branch: %s/%s/%s with path %v\n", targetOwner, repo, branch, path) + + escapedBranch, _ := url.PathUnescape(branch) + if escapedBranch == "" { + escapedBranch = branch + } + // Check if the branch exists, otherwise treat it as a file path + targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, repo, branch) + fmt.Printf("Branch %s has timestamp %v\n", targetBranch, targetOptions.BranchTimestamp) + if targetOptions.BranchTimestamp != (time.Time{}) { + // Branch exists, use it + targetRepo = repo + targetPath = strings.Trim(strings.Join(path, "/"), "/") + + if canonicalLink != "" { + // Hide from search machines & add canonical link + ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") + ctx.Response.Header.Set("Link", + strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ + "; rel=\"canonical\"", + ) + } + + return true + } else { + // branch doesn't exist + return false + } + } + + // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. + var tryUpstream = func() { + // Try to request the file from the Gitea API + if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { + returnErrorPage(ctx, ctx.Response.StatusCode()) + } + } + if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { // Serve raw content from RawDomain - // TODO: add canonical link and "X-Robots-Tag: noarchive, noindex" - targetOptions.TryIndexPages = false targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} targetOptions.DefaultMimeType = "text/plain; charset=utf-8" - pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 4) + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") if len(pathElements) < 2 { // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) @@ -80,46 +122,74 @@ func handler(ctx *fasthttp.RequestCtx) { } targetOwner = pathElements[0] targetRepo = pathElements[1] - if len(pathElements) > 3 { - targetPath = strings.Trim(pathElements[2]+"/"+pathElements[3], "/") - } else if len(pathElements) > 2 { - targetPath = pathElements[2] - } // raw.codeberg.page/example/myrepo/@main/index.html - if len(pathElements) > 3 && strings.HasPrefix(pathElements[2], "@") { - branch, _ := url.PathUnescape(pathElements[2][1:]) - if branch == "" { - branch = pathElements[2][1:] - } - // Check if the branch exists, otherwise treat it as a file path - targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, branch) - if targetOptions.BranchTimestamp != (time.Time{}) { - targetPath = strings.Trim(pathElements[3], "/") // branch exists, use it - } else { - targetBranch = "" // branch doesn't exist, use default branch + if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { + if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + ) { + tryUpstream() + return } + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } else { + tryBranch(targetRepo, "", pathElements[2:], + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + ) + tryUpstream() + return } } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix - // TODO: add @branch syntax with "X-Robots-Tag: noarchive, noindex" - - pathElements := strings.SplitN(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/", 2) + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) targetRepo = pathElements[0] - if len(pathElements) > 1 { - targetPath = strings.Trim(pathElements[1], "/") + targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") + + // Check if the first directory is a repo with the second directory as a branch + // example.codeberg.page/myrepo/@main/index.html + if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { + if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], + "/"+pathElements[0]+"/%p", + ) { + tryUpstream() + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + } + return + } + + // Check if the first directory is a branch for the "pages" repo + // example.codeberg.page/@main/index.html + if strings.HasPrefix(pathElements[0], "@") { + if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { + tryUpstream() + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + } + return } // Check if the first directory is a repo with a "pages" branch - targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, "pages") - if targetOptions.BranchTimestamp == (time.Time{}) { - targetRepo = "pages" - targetBranch = "" - targetPath = strings.Trim(pathElements[0]+"/"+targetPath, "/") + // example.codeberg.page/myrepo/index.html + if tryBranch(pathElements[0], "pages", pathElements[1:], "") { + tryUpstream() + return } + + // Try to use the "pages" repo on its default branch + // example.codeberg.page/index.html + if tryBranch("pages", "", pathElements, "") { + tryUpstream() + return + } + + // Couldn't find a valid repo/branch + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return } else { // Serve pages from external domains @@ -129,23 +199,6 @@ func handler(ctx *fasthttp.RequestCtx) { return } } - - // Check if a username can't exist because it's reserved (we'd risk to hit a Gitea route in that case) - if _, ok := ReservedUsernames[targetOwner]; ok { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - - // Check for blob path - if strings.HasPrefix(targetPath, "blob/") { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - - // Try to request the file from the Gitea API - if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { - returnErrorPage(ctx, ctx.Response.StatusCode()) - } } // returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced @@ -153,7 +206,7 @@ func handler(ctx *fasthttp.RequestCtx) { func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetStatusCode(code) ctx.Response.Header.SetContentType("text/html; charset=utf-8") - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code) + " " + fasthttp.StatusMessage(code)))) + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+fasthttp.StatusMessage(code)))) } // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch @@ -163,8 +216,9 @@ func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, branchWithFallback = branch if branch == "" { var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+url.PathEscape(owner)+"/"+url.PathEscape(repo), 10*time.Second) if err != nil || status != 200 { + fmt.Printf("Default branch request to Gitea API failed with status code %d and error %s\n", status, err) branchWithFallback = "" return } @@ -173,8 +227,9 @@ func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+url.PathEscape(owner)+"/"+url.PathEscape(repo)+"/branches/"+url.PathEscape(branch), 10*time.Second) if err != nil || status != 200 { + fmt.Printf("Branch info request to Gitea API failed with status code %d and error %s\n", status, err) branchWithFallback = "" return } @@ -196,12 +251,17 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // Handle repositories with no/broken pages setup if options.BranchTimestamp == (time.Time{}) || targetBranch == "" { - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + ctx.Response.SetStatusCode(fasthttp.StatusFailedDependency) ctx.Response.Header.SetContentType("text/html; charset=utf-8") ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte("pages not set up for this repo"))) return true } + if targetOwner == "" || targetRepo == "" || targetBranch == "" { + returnErrorPage(ctx, fasthttp.StatusBadRequest) + return true + } + // Check if the browser has a cached version if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { if !ifModifiedSince.Before(options.BranchTimestamp) { @@ -212,7 +272,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // Make a GET request to the upstream URL req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + url.PathEscape(targetOwner) + "/" + url.PathEscape(targetRepo) + "/raw/" + url.PathEscape(targetBranch) + "/" + url.PathEscape(targetPath)) res := fasthttp.AcquireResponse() err := fasthttp.DoTimeout(req, res, 10*time.Second) diff --git a/main.go b/main.go index ae4ac7b..a7081a4 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,6 @@ import ( "fmt" "net" "os" - "strings" "time" _ "embed" @@ -67,14 +66,6 @@ var IndexPages = []string{ "index.html", } -// ReservedUsernames specifies the usernames that are reserved by Gitea and thus may not be used as owner names. -// The contents are taken from https://github.com/go-gitea/gitea/blob/master/models/user.go#L783; reserved names with -// dots are removed as they are forbidden for Codeberg Pages anyways. -var ReservedUsernames = createLookupMapFromWords(` - admin api assets attachments avatars captcha commits debug error explore ghost help install issues less login metrics milestones new notifications org plugins pulls raw repo search stars template user - -`) - // main sets up and starts the web server. func main() { // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash @@ -122,14 +113,3 @@ func envOr(env string, or string) string { } return or } - -func createLookupMapFromWords(input string) map[string]struct{} { - var res = map[string]struct{}{} - input = strings.NewReplacer("\t", " ", "\n", " ", "\r", " ").Replace(input) - for _, word := range strings.Split(input, " ") { - if len(word) > 0 { - res[word] = struct{}{} - } - } - return res -} From 203e230905a73524b3f9b251bcb7de12c15208a5 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 19 Mar 2021 20:58:53 +0100 Subject: [PATCH 011/320] Implement caching & limit concurrency --- go.mod | 1 + go.sum | 2 + handler.go | 106 ++++++++++++++++++++++++++++------------------------- main.go | 2 + 4 files changed, 62 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index 8f7b835..b467059 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module codeberg.org/codeberg/pages go 1.16 require ( + github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/valyala/fasthttp v1.22.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index 63178eb..fd150c5 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= +github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/klauspost/compress v1.11.8 h1:difgzQsp5mdAz9v8lm3P/I+EpDKMU/6uTMw1y1FObuo= diff --git a/handler.go b/handler.go index adf5c25..f837e7f 100644 --- a/handler.go +++ b/handler.go @@ -3,10 +3,10 @@ package main import ( "bytes" "fmt" + "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" "github.com/valyala/fastjson" "mime" - "net/url" "path" "strconv" "strings" @@ -69,34 +69,30 @@ func handler(ctx *fasthttp.RequestCtx) { if repo == "" { return false } - fmt.Printf("Trying branch: %s/%s/%s with path %v\n", targetOwner, repo, branch, path) - escapedBranch, _ := url.PathUnescape(branch) - if escapedBranch == "" { - escapedBranch = branch - } // Check if the branch exists, otherwise treat it as a file path - targetBranch, targetOptions.BranchTimestamp = getBranchTimestamp(targetOwner, repo, branch) - fmt.Printf("Branch %s has timestamp %v\n", targetBranch, targetOptions.BranchTimestamp) - if targetOptions.BranchTimestamp != (time.Time{}) { - // Branch exists, use it - targetRepo = repo - targetPath = strings.Trim(strings.Join(path, "/"), "/") - - if canonicalLink != "" { - // Hide from search machines & add canonical link - ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") - ctx.Response.Header.Set("Link", - strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ - "; rel=\"canonical\"", - ) - } - - return true - } else { + branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch) + if branchTimestampResult == nil { // branch doesn't exist return false } + + // Branch exists, use it + targetRepo = repo + targetPath = strings.Trim(strings.Join(path, "/"), "/") + targetBranch = branchTimestampResult.branch + targetOptions.BranchTimestamp = branchTimestampResult.timestamp + + if canonicalLink != "" { + // Hide from search machines & add canonical link + ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") + ctx.Response.Header.Set("Link", + strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ + "; rel=\"canonical\"", + ) + } + + return true } // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. @@ -209,36 +205,49 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+fasthttp.StatusMessage(code)))) } +type branchTimestamp struct { + branch string + timestamp time.Time +} +var branchTimestampCache = mcache.New() + // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or an empty time.Time if the branch doesn't exist) -// TODO: cache responses for ~15 minutes if a branch exists -func getBranchTimestamp(owner, repo, branch string) (branchWithFallback string, t time.Time) { - branchWithFallback = branch +func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { + if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { + return result.(*branchTimestamp) + } + result := &branchTimestamp{} + result.branch = branch if branch == "" { var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+url.PathEscape(owner)+"/"+url.PathEscape(repo), 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) if err != nil || status != 200 { - fmt.Printf("Default branch request to Gitea API failed with status code %d and error %s\n", status, err) - branchWithFallback = "" - return + return nil } branch = fastjson.GetString(body, "default_branch") - branchWithFallback = branch + result.branch = branch } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+url.PathEscape(owner)+"/"+url.PathEscape(repo)+"/branches/"+url.PathEscape(branch), 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) if err != nil || status != 200 { - fmt.Printf("Branch info request to Gitea API failed with status code %d and error %s\n", status, err) - branchWithFallback = "" - return + return nil } - t, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - return + result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, result, 15 * time.Second) + return result } -// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. +var upstreamClient = fasthttp.Client{ + ReadTimeout: 10 * time.Second, + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 1024 * 16, // TODO: adjust bottlenecks for best performance with Gitea! +} + + // upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (success bool) { if options.ForbiddenMimeTypes == nil { options.ForbiddenMimeTypes = map[string]struct{}{} @@ -246,15 +255,14 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // Check if the branch exists and when it was modified if options.BranchTimestamp == (time.Time{}) { - targetBranch, options.BranchTimestamp = getBranchTimestamp(targetOwner, targetRepo, targetBranch) - } + branch := getBranchTimestamp(targetOwner, targetRepo, targetBranch) - // Handle repositories with no/broken pages setup - if options.BranchTimestamp == (time.Time{}) || targetBranch == "" { - ctx.Response.SetStatusCode(fasthttp.StatusFailedDependency) - ctx.Response.Header.SetContentType("text/html; charset=utf-8") - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte("pages not set up for this repo"))) - return true + if branch == nil { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return true + } + targetBranch = branch.branch + options.BranchTimestamp = branch.timestamp } if targetOwner == "" || targetRepo == "" || targetBranch == "" { @@ -272,9 +280,9 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // Make a GET request to the upstream URL req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + url.PathEscape(targetOwner) + "/" + url.PathEscape(targetRepo) + "/raw/" + url.PathEscape(targetBranch) + "/" + url.PathEscape(targetPath)) + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) res := fasthttp.AcquireResponse() - err := fasthttp.DoTimeout(req, res, 10*time.Second) + err := upstreamClient.Do(req, res) // Handle errors if res.StatusCode() == fasthttp.StatusNotFound { diff --git a/main.go b/main.go index a7081a4..ad1c40e 100644 --- a/main.go +++ b/main.go @@ -99,6 +99,8 @@ func main() { NoDefaultServerHeader: true, NoDefaultDate: true, ReadTimeout: 10 * time.Second, + Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! + MaxConnsPerIP: 100, }).Serve(listener) if err != nil { fmt.Printf("Couldn't start server: %s\n", err) From 2e0608c270ed9fb980b3571a91a9d6637586eb08 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 19 Mar 2021 21:33:57 +0100 Subject: [PATCH 012/320] Fix redirects for pages repo --- handler.go | 9 ++++++++- main.go | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index f837e7f..8cb5754 100644 --- a/handler.go +++ b/handler.go @@ -148,6 +148,12 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a repo with the second directory as a branch // example.codeberg.page/myrepo/@main/index.html if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { + if targetRepo == "pages" { + // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... + ctx.Redirect("/" + strings.Join(pathElements[1:], "/"), fasthttp.StatusMovedPermanently) + return + } + if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { @@ -171,7 +177,8 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a repo with a "pages" branch // example.codeberg.page/myrepo/index.html - if tryBranch(pathElements[0], "pages", pathElements[1:], "") { + // example.codeberg.page/pages/... is not allowed here. + if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { tryUpstream() return } diff --git a/main.go b/main.go index ad1c40e..95ec29b 100644 --- a/main.go +++ b/main.go @@ -2,11 +2,13 @@ // // Mapping custom domains is not static anymore, but can be done with DNS: // -// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. +// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. The +// first line will be the canonical domain/URL; all other occurrences will be redirected to it. // // 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to // "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): // www.example.org. IN CNAME main.pages.example.codeberg.page. +// // 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record // for "example.org" (if your provider allows ALIAS or similar records): // example.org IN ALIAS codeberg.page. From b54f9df4c509462baa4c7b13b30f62cb787966eb Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 6 Jun 2021 12:29:53 +0200 Subject: [PATCH 013/320] Add proof of concept for SNI-based routing through HAProxy --- haproxy-sni/.gitignore | 1 + haproxy-sni/README.md | 20 ++++ haproxy-sni/dhparam.pem | 8 ++ haproxy-sni/docker-compose.yml | 22 +++++ haproxy-sni/gitea-www/index.html | 1 + haproxy-sni/gitea.Caddyfile | 3 + .../haproxy-certificates/codeberg.org.pem | 26 +++++ .../haproxy-certificates/codeberg.org.pem.key | 28 ++++++ haproxy-sni/haproxy.cfg | 98 +++++++++++++++++++ haproxy-sni/pages-www/index.html | 1 + haproxy-sni/pages.Caddyfile | 4 + haproxy-sni/test.sh | 22 +++++ 12 files changed, 234 insertions(+) create mode 100644 haproxy-sni/.gitignore create mode 100644 haproxy-sni/README.md create mode 100644 haproxy-sni/dhparam.pem create mode 100644 haproxy-sni/docker-compose.yml create mode 100644 haproxy-sni/gitea-www/index.html create mode 100644 haproxy-sni/gitea.Caddyfile create mode 100644 haproxy-sni/haproxy-certificates/codeberg.org.pem create mode 100644 haproxy-sni/haproxy-certificates/codeberg.org.pem.key create mode 100644 haproxy-sni/haproxy.cfg create mode 100644 haproxy-sni/pages-www/index.html create mode 100644 haproxy-sni/pages.Caddyfile create mode 100755 haproxy-sni/test.sh diff --git a/haproxy-sni/.gitignore b/haproxy-sni/.gitignore new file mode 100644 index 0000000..2232829 --- /dev/null +++ b/haproxy-sni/.gitignore @@ -0,0 +1 @@ +*.dump diff --git a/haproxy-sni/README.md b/haproxy-sni/README.md new file mode 100644 index 0000000..9d7de21 --- /dev/null +++ b/haproxy-sni/README.md @@ -0,0 +1,20 @@ +# HAProxy with SNI & Host-based rules + +This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), *as well as* to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection). + +## How it works +1. The `http_redirect_frontend` is only there to listen on port 80 and redirect every request to HTTPS. +2. The `https_sni_frontend` listens on port 443 and chooses a backend based on the SNI hostname of the TLS connection. +3. The `https_termination_backend` passes all requests to a unix socket (using the plain TCP data). +4. The `https_termination_frontend` listens on said unix socket, terminates the HTTPS connections and then chooses a backend based on the Host header. + +In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listening via HTTPS and is providing its own HTTPS certificates, while the `gitea_backend` only provides HTTP. + +## How to test +```bash +docker-compose up -d +./test.sh + +# For manual testing: all HTTPS URLs connect to localhost:443 & certificates are not verified. +./test.sh [curl-options...] +``` diff --git a/haproxy-sni/dhparam.pem b/haproxy-sni/dhparam.pem new file mode 100644 index 0000000..088f967 --- /dev/null +++ b/haproxy-sni/dhparam.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- \ No newline at end of file diff --git a/haproxy-sni/docker-compose.yml b/haproxy-sni/docker-compose.yml new file mode 100644 index 0000000..4dd8677 --- /dev/null +++ b/haproxy-sni/docker-compose.yml @@ -0,0 +1,22 @@ +version: "3" +services: + haproxy: + image: haproxy + ports: ["443:443"] + volumes: + - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + - ./dhparam.pem:/etc/ssl/dhparam.pem:ro + - ./haproxy-certificates:/etc/ssl/private/haproxy:ro + cap_add: + - NET_ADMIN + gitea: + image: caddy + volumes: + - ./gitea-www:/srv:ro + - ./gitea.Caddyfile:/etc/caddy/Caddyfile:ro + pages: + image: caddy + volumes: + - ./pages-www:/srv:ro + - ./pages.Caddyfile:/etc/caddy/Caddyfile:ro + diff --git a/haproxy-sni/gitea-www/index.html b/haproxy-sni/gitea-www/index.html new file mode 100644 index 0000000..d092750 --- /dev/null +++ b/haproxy-sni/gitea-www/index.html @@ -0,0 +1 @@ +Hello to Gitea! diff --git a/haproxy-sni/gitea.Caddyfile b/haproxy-sni/gitea.Caddyfile new file mode 100644 index 0000000..e92a157 --- /dev/null +++ b/haproxy-sni/gitea.Caddyfile @@ -0,0 +1,3 @@ +http://codeberg.org + +file_server diff --git a/haproxy-sni/haproxy-certificates/codeberg.org.pem b/haproxy-sni/haproxy-certificates/codeberg.org.pem new file mode 100644 index 0000000..e85b673 --- /dev/null +++ b/haproxy-sni/haproxy-certificates/codeberg.org.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUDCCArigAwIBAgIRAMq3iwF963VGkzXFpbrpAtkwDQYJKoZIhvcNAQELBQAw +gYkxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEvMC0GA1UECwwmbW9t +YXJAbW9yaXR6LWxhcHRvcCAoTW9yaXR6IE1hcnF1YXJkdCkxNjA0BgNVBAMMLW1r +Y2VydCBtb21hckBtb3JpdHotbGFwdG9wIChNb3JpdHogTWFycXVhcmR0KTAeFw0y +MTA2MDYwOTQ4NDFaFw0yMzA5MDYwOTQ4NDFaMFoxJzAlBgNVBAoTHm1rY2VydCBk +ZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEvMC0GA1UECwwmbW9tYXJAbW9yaXR6LWxh +cHRvcCAoTW9yaXR6IE1hcnF1YXJkdCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCrSPSPM6grNZMG4ZKFCVxuXu+qkHdzSR96QUxi00VkIrkGPmyMN7q7 +rUQJto9C9guJio3n7y3Bvr5kBjICjyWQd7GfkVuYgiYiG/O2hy1u1dIMCAB/Zhx1 +F1mvRfn/Q4eZk2GSOUM+kC0xaNsn2827VGLOGFywUhRmu7J9QSQ3x1Pi5BME7eNC +AKup0CbrMrZSzKAEuYujLY0UYRxUrguMnV60wxJDCYE14YDxn9t0g7wQmzyndupk +AMLNJZX5L83RA6vUEuTVYBFcyB0Fu3oBLQ31y5QOZ7WF/QiO5cPicQJI/oyXlHq4 +97BWS/H28kj1H5ZM8+5yhCYDtgj7dERpAgMBAAGjYTBfMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSOSXQZqt2gjbTOkE9Q +ddI8SYPqrDAXBgNVHREEEDAOggxjb2RlYmVyZy5vcmcwDQYJKoZIhvcNAQELBQAD +ggGBAJ/57DGqfuOa3aS/nLeAzl8komvyHuoOZi9yDK2Jqr+COxP58zSu8xwhiZfc +TJvIyB9QR7imGiQ7fEKby40q8uxGGx13oY7gQy7PG8hHk2dkfDZuSQacnpPRC3W0 +0dL2CQIog6rw6jJHjxneitkX9FUmOnHIKy7LHya0Sthg36Z0Qw5JA3SCy6OQNepR +R2XzwTZ0KFk6gAuKCto8ENUlU5lV9PM4X3U0cBOIc5LJAPM+cxEDUocFtFqKJPbe +YYlSeB200YhYOdi+x34n9xnQjFu/jVlWF+Y0tMBB1WWq6rZbnuylwWLYQZAo10Co +D3oWsYRlD/ZL7X20ztIy8vRXz33ugnxxf88Q7csWDYb4S325svLfI2EjciIxYmBo +dSJxXRQkadjIoI7gNvzeWBkYSJpQUbaD4nT2xRS8vfuv42/DrIehb8SbTivHmcB3 +OibpWIvDtS1B8thIlzl0edb+8pb6mof7pOBxoZdcBsSAk2/48s+jfRHfD9XcuKnv +hGCdSQ== +-----END CERTIFICATE----- diff --git a/haproxy-sni/haproxy-certificates/codeberg.org.pem.key b/haproxy-sni/haproxy-certificates/codeberg.org.pem.key new file mode 100644 index 0000000..b9c4d61 --- /dev/null +++ b/haproxy-sni/haproxy-certificates/codeberg.org.pem.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrSPSPM6grNZMG +4ZKFCVxuXu+qkHdzSR96QUxi00VkIrkGPmyMN7q7rUQJto9C9guJio3n7y3Bvr5k +BjICjyWQd7GfkVuYgiYiG/O2hy1u1dIMCAB/Zhx1F1mvRfn/Q4eZk2GSOUM+kC0x +aNsn2827VGLOGFywUhRmu7J9QSQ3x1Pi5BME7eNCAKup0CbrMrZSzKAEuYujLY0U +YRxUrguMnV60wxJDCYE14YDxn9t0g7wQmzyndupkAMLNJZX5L83RA6vUEuTVYBFc +yB0Fu3oBLQ31y5QOZ7WF/QiO5cPicQJI/oyXlHq497BWS/H28kj1H5ZM8+5yhCYD +tgj7dERpAgMBAAECggEAAeW+/88cr83aIRtimiKuaXKXyRXsnNRUivAqPnYEsMVJ +s24BmdQMN4QF2u2wzJcZLZ7hT45wvVK1nToMV8bqLZ2F1DSyBRB8B6iznHQG5tFr +kEKObtrcuddWYQCvckp3OBZP4GTN/+Vs+r0koF5o+whGR+4xKKrgGvs9UPHlytBf +0DMzAzWzGPp6qBPw2sUx/fa9r5TqFW+p4SEOZJUqL2/zEZ6KBWbKw5T1e1y2kMEc +cquUQ4avqK/N1nwRNKUnTvW827v0k7HQ2cFdrjIATNlICslOWJQicG5GUOuSBkTC +0FFkSTtHP4qm0BqShjv6NDmzX+3WCVkGOKFOI+zuWQKBgQDBq8yEcvfMJY98KNlR +eKKdJAMJvKdoD65Yv6EG7ZzpeEWHaTGhu71RPgHYkHn8h1T/9WniroSk19+zb4lP +mMsBwxpg5HejWPzIiiJRkRCRA7aZZfvaXfIWryB4kI1tlGHBNN/+SYpG1zdNumtp +Xyb/sQWMMWRZdRgclF8V+NvduwKBgQDiaM59gBROleREduFZE1a0oXtt+CrwrPlz +hclrkYl1FbTA4TdL4JNbj5jCXCR8YakFhxWEmhwq+Dgl1NQY/YjHyG3w2imaeASX +QUsEvAIvNrv1mIELiYCLmUElyX4WL3UhqveOFcZUvR1Z4TTwruPQmXf6BJEBLbWI +f7odmG6yKwKBgQCzpuLjZiZY9+qe2OGmQopNzE8JJDgCPrGS38fGvnnU1N1iXAFP +LvDRwPxDYNnXl84QVR2wygR/SUTYlTlBXdHKw6nfgW89Vlm+yOxGz5MXgeNLbp/u +k0DzK+aqECUxJfh8GclCgANF7XP+pVPn/f0WKKalwld86DLCqBuALUX+6wKBgCUh +gxvZ8Xqh4nnH9VUicsnU4eU7Ge+2roJfopTdnWlyUd6AEQ2EmyYc+rSFYAZ2Db42 +VTUWASCa7LpnmREwI0qAeGdToBcRL8+OibsRClqr409331IBDu/WBnUoAmGpDtCi +tU68C3bCPRoMcR430GzZfm+maBGFaYwlRmSsJxtZAoGADSA3uAZBuWNDPNKUas2k +Z2dXFEPNpViMjQzJ+Ko7lbOBpUUUQfZF2VMSK4lcnhhbmhcMrYzWWmh6uaw78aHY +e3M//BfcVMdxHw7EemGOViNNq3uDIwzvYteoe6fAOA7MaV+WjJaf+smceR4o38fk +U9RTkKpRJIcvEW5bvTI9h4o= +-----END PRIVATE KEY----- diff --git a/haproxy-sni/haproxy.cfg b/haproxy-sni/haproxy.cfg new file mode 100644 index 0000000..869bae3 --- /dev/null +++ b/haproxy-sni/haproxy.cfg @@ -0,0 +1,98 @@ +##################################### +## Global Configuration & Defaults ## +##################################### + +global + log stderr format iso local7 + + # generated 2021-06-05, Mozilla Guideline v5.6, HAProxy 2.1, OpenSSL 1.1.1d, intermediate configuration + # https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.1d&guideline=5.6 + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam + ssl-dh-param-file /etc/ssl/dhparam.pem + +defaults + log global + timeout connect 30000 + timeout check 300000 + timeout client 300000 + timeout server 300000 + +############################################################################ +## Frontends: HTTP; HTTPS → HTTPS SNI-based; HTTPS → HTTP(S) header-based ## +############################################################################ + +frontend http_redirect_frontend + # HTTP backend to redirect everything to HTTPS + bind :::80 v4v6 + mode http + http-request redirect scheme https + +frontend https_sni_frontend + # TCP backend to forward to HTTPS backends based on SNI + bind :::443 v4v6 + mode tcp + + # Wait up to 5s for a SNI header & only accept TLS connections + tcp-request inspect-delay 5s + tcp-request content capture req.ssl_sni len 255 + log-format "%ci:%cp -> %[capture.req.hdr(0)] @ %f (%fi:%fp) -> %b (%bi:%bp)" + tcp-request content accept if { req.ssl_hello_type 1 } + + ################################################### + ## Rules: forward to HTTPS(S) header-based rules ## + ################################################### + acl use_http_backend req.ssl_sni -i "codeberg.org" + acl use_http_backend req.ssl_sni -i "join.codeberg.org" + use_backend https_termination_backend if use_http_backend + + ############################ + ## Rules: HTTPS SNI-based ## + ############################ + # use_backend xyz_backend if { req.ssl_sni -i "xyz" } + default_backend pages_backend + +frontend https_termination_frontend + # Terminate TLS for HTTP backends + bind /tmp/haproxy-tls-termination.sock accept-proxy ssl strict-sni alpn h2,http/1.1 crt /etc/ssl/private/haproxy/ + mode http + + # HSTS (63072000 seconds) + http-response set-header Strict-Transport-Security max-age=63072000 + + http-request capture req.hdr(Host) len 255 + log-format "%ci:%cp -> %[capture.req.hdr(0)] @ %f (%fi:%fp) -> %b (%bi:%bp)" + + ################################## + ## Rules: HTTPS(S) header-based ## + ################################## + use_backend gitea_backend if { hdr(host) -i codeberg.org } + +backend https_termination_backend + # Redirect to the terminating HTTPS frontend for all HTTP backends + server https_termination_server /tmp/haproxy-tls-termination.sock send-proxy-v2-ssl-cn + mode tcp + +############################### +## Backends: HTTPS SNI-based ## +############################### + +backend pages_backend + # Pages server is a HTTP backend that uses its own certificates for custom domains + server pages_server pages:443 + mode tcp + +#################################### +## Backends: HTTP(S) header-based ## +#################################### + +backend gitea_backend + server gitea_server gitea:80 + mode http diff --git a/haproxy-sni/pages-www/index.html b/haproxy-sni/pages-www/index.html new file mode 100644 index 0000000..dc24785 --- /dev/null +++ b/haproxy-sni/pages-www/index.html @@ -0,0 +1 @@ +Hello to Pages! diff --git a/haproxy-sni/pages.Caddyfile b/haproxy-sni/pages.Caddyfile new file mode 100644 index 0000000..17adc19 --- /dev/null +++ b/haproxy-sni/pages.Caddyfile @@ -0,0 +1,4 @@ +https://example-page.org + +tls internal +file_server diff --git a/haproxy-sni/test.sh b/haproxy-sni/test.sh new file mode 100755 index 0000000..89e2dfd --- /dev/null +++ b/haproxy-sni/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh +if [ $# -gt 0 ]; then + exec curl -k --resolve '*:443:127.0.0.1' "$@" +fi + +fail() { + echo "[FAIL] $@" + exit 1 +} + +echo "Connecting to Gitea..." +res=$(curl https://codeberg.org -sk --resolve '*:443:127.0.0.1' --trace-ascii gitea.dump | tee /dev/stderr) +echo "$res" | grep -Fx 'Hello to Gitea!' >/dev/null || fail "Gitea didn't answer" +grep '^== Info: issuer: O=mkcert development CA;' gitea.dump || { grep grep '^== Info: issuer:' gitea.dump; fail "Gitea didn't use the correct certificate!"; } + +echo "Connecting to Pages..." +res=$(curl https://example-page.org -sk --resolve '*:443:127.0.0.1' --trace-ascii pages.dump | tee /dev/stderr) +echo "$res" | grep -Fx 'Hello to Pages!' >/dev/null || fail "Pages didn't answer" +grep '^== Info: issuer: CN=Caddy Local Authority\b' pages.dump || { grep '^== Info: issuer:' pages.dump; fail "Pages didn't use the correct certificate!"; } + +echo "All tests succeeded" +rm *.dump From e94bdb4ed3c6c5fe23a22fa652f80542a36a4d2f Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 6 Jun 2021 12:38:07 +0200 Subject: [PATCH 014/320] Add screenshot of the SNI test script --- haproxy-sni/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/haproxy-sni/README.md b/haproxy-sni/README.md index 9d7de21..8bb8f02 100644 --- a/haproxy-sni/README.md +++ b/haproxy-sni/README.md @@ -12,9 +12,12 @@ In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listenin ## How to test ```bash -docker-compose up -d +docker-compose up & ./test.sh +docker-compose down # For manual testing: all HTTPS URLs connect to localhost:443 & certificates are not verified. ./test.sh [curl-options...] ``` + +![Screenshot of the test script's output](/attachments/c82d79ea-7586-4d4b-b340-3ad0030185d6) From 4bc1cd5f7bcadf16d6108612ea06b132833abdcb Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 8 Jul 2021 23:08:30 +0200 Subject: [PATCH 015/320] Add file cache --- handler.go | 69 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/handler.go b/handler.go index 8cb5754..7deae03 100644 --- a/handler.go +++ b/handler.go @@ -6,6 +6,7 @@ import ( "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" "github.com/valyala/fastjson" + "io" "mime" "path" "strconv" @@ -212,11 +213,25 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+fasthttp.StatusMessage(code)))) } +// BranchCacheTimeout specifies the timeout for the branch timestamp cache. +var BranchCacheTimeout = 60*time.Second +// branchTimestampCache stores branch timestamps for faster cache checking +var branchTimestampCache = mcache.New() type branchTimestamp struct { branch string timestamp time.Time } -var branchTimestampCache = mcache.New() + +// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this shorter +// than BranchCacheTimeout when running out of memory. +var FileCacheTimeout = 60*time.Second +// fileResponseCache stores responses from the Gitea server +var fileResponseCache = mcache.New() +type fileResponse struct { + exists bool + mimeType string + body []byte +} // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or an empty time.Time if the branch doesn't exist) @@ -228,16 +243,15 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { result.branch = branch if branch == "" { var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, BranchCacheTimeout) if err != nil || status != 200 { return nil } - branch = fastjson.GetString(body, "default_branch") - result.branch = branch + result.branch = fastjson.GetString(body, "default_branch") } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 10*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, BranchCacheTimeout) if err != nil || status != 200 { return nil } @@ -251,11 +265,11 @@ var upstreamClient = fasthttp.Client{ ReadTimeout: 10 * time.Second, MaxConnDuration: 60 * time.Second, MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 1024 * 16, // TODO: adjust bottlenecks for best performance with Gitea! + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! } - // upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (success bool) { +// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. +func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (final bool) { if options.ForbiddenMimeTypes == nil { options.ForbiddenMimeTypes = map[string]struct{}{} } @@ -289,10 +303,18 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t req := fasthttp.AcquireRequest() req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) res := fasthttp.AcquireResponse() - err := upstreamClient.Do(req, res) + isCached := false + var cachedResponse fileResponse + var err error + if cachedValue, ok := fileResponseCache.Get(string(req.RequestURI())); ok { + isCached = true + cachedResponse = cachedValue.(fileResponse) + } else { + err = upstreamClient.Do(req, res) + } // Handle errors - if res.StatusCode() == fasthttp.StatusNotFound { + if (isCached && !cachedResponse.exists) || res.StatusCode() == fasthttp.StatusNotFound { if options.TryIndexPages { // copy the options struct & try if an index page exists optionsForIndexPages := *options @@ -305,15 +327,22 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t } } ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + if !isCached { + // Update cache if the request is fresh + _ = fileResponseCache.Set(string(req.RequestURI()), fileResponse{ + exists: false, + }, FileCacheTimeout) + } return false } - if err != nil || res.StatusCode() != fasthttp.StatusOK { + if !isCached && (err != nil || res.StatusCode() != fasthttp.StatusOK) { fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) returnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } // Append trailing slash if missing (for index files) + // options.AppendTrailingSlash is only true when looking for index pages if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) return true @@ -331,16 +360,30 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t } ctx.Response.Header.SetContentType(mimeType) - // Write the response to the original request + // Everything's okay so far ctx.Response.SetStatusCode(fasthttp.StatusOK) ctx.Response.Header.SetLastModified(options.BranchTimestamp) - err = res.BodyWriteTo(ctx.Response.BodyWriter()) + + // Write the response body to the original request + var cacheBodyWriter bytes.Buffer + if !isCached { + err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + } else { + _, err = ctx.Write(cachedResponse.body) + } if err != nil { fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) returnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } + if !isCached { + cachedResponse.exists = true + cachedResponse.mimeType = mimeType + cachedResponse.body = cacheBodyWriter.Bytes() + _ = fileResponseCache.Set(string(req.RequestURI()), cachedResponse, FileCacheTimeout) + } + return true } From 3e9aaa8a4a8b8a1011a118bcf8924066d4971117 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 8 Jul 2021 23:08:50 +0200 Subject: [PATCH 016/320] Make settings configurable --- main.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 95ec29b..60aad97 100644 --- a/main.go +++ b/main.go @@ -32,24 +32,24 @@ import ( // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". -var MainDomainSuffix = []byte(".codeberg.page") +var MainDomainSuffix = []byte("." + envOr("PAGES_DOMAIN", "codeberg.page")) // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. -var GiteaRoot = []byte("https://codeberg.org") +var GiteaRoot = []byte(envOr("GITEA_ROOT", "https://codeberg.org")) //go:embed 404.html var NotFoundPage []byte // BrokenDNSPage will be shown (with a redirect) when trying to access a domain for which no DNS CNAME record exists. -var BrokenDNSPage = "https://docs.codeberg.org/codeberg-pages/custom-domains/" +var BrokenDNSPage = envOr("REDIRECT_BROKEN_DNS", "https://docs.codeberg.org/codeberg-pages/custom-domains/") // RawDomain specifies the domain from which raw repository content shall be served in the following format: // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} // (set to []byte(nil) to disable raw content hosting) -var RawDomain = []byte("raw.codeberg.page") +var RawDomain = []byte(envOr("RAW_DOMAIN", "raw.codeberg.org")) // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). -var RawInfoPage = "https://docs.codeberg.org/codeberg-pages/raw-content/" +var RawInfoPage = envOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/codeberg-pages/raw-content/") // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. var AllowedCorsDomains = [][]byte{ @@ -66,6 +66,8 @@ var BlacklistedPaths = [][]byte{ // IndexPages lists pages that may be considered as index pages for directories. var IndexPages = []string{ "index.html", + "index.htm", + "README.md", } // main sets up and starts the web server. From f63bd1bb2f299b1b0b80c50157931937b480531d Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 8 Jul 2021 23:08:58 +0200 Subject: [PATCH 017/320] Add timing tests --- handler_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 handler_test.go diff --git a/handler_test.go b/handler_test.go new file mode 100644 index 0000000..3a6d4b2 --- /dev/null +++ b/handler_test.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "github.com/valyala/fasthttp" + "testing" + "time" +) + +func TestHandlerPerformance(t *testing.T) { + ctx := &fasthttp.RequestCtx{ + Request: *fasthttp.AcquireRequest(), + Response: *fasthttp.AcquireResponse(), + } + ctx.Request.SetRequestURI("http://mondstern.codeberg:8080/") + fmt.Printf("Start: %v\n", time.Now()) + start := time.Now() + handler(ctx) + end := time.Now() + fmt.Printf("Done: %v\n", time.Now()) + if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { + t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) + } else { + t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + } + + ctx.Response.Reset() + ctx.Response.ResetBody() + fmt.Printf("Start: %v\n", time.Now()) + start = time.Now() + handler(ctx) + end = time.Now() + fmt.Printf("Done: %v\n", time.Now()) + if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { + t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) + } else { + t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + } +} From 373c13baee13c7d18a5fcfdaa2025e74ff164717 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 8 Jul 2021 23:09:46 +0200 Subject: [PATCH 018/320] Update dependencies --- go.mod | 4 +++- go.sum | 33 ++++++++++++++++----------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index b467059..b5102c7 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.16 require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a - github.com/valyala/fasthttp v1.22.0 + github.com/andybalholm/brotli v1.0.3 // indirect + github.com/klauspost/compress v1.13.1 // indirect + github.com/valyala/fasthttp v1.28.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index fd150c5..61d068d 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,26 @@ github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= -github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc= -github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/klauspost/compress v1.11.8 h1:difgzQsp5mdAz9v8lm3P/I+EpDKMU/6uTMw1y1FObuo= -github.com/klauspost/compress v1.11.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= +github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= +github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.22.0 h1:OpwH5KDOJ9cS2bq8fD+KfT4IrksK0llvkHf4MZx42jQ= -github.com/valyala/fasthttp v1.22.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= +github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= +github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20210226101413-39120d07d75e/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 13b386d44259251641b4297e44d757d741d3dbbf Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 9 Jul 2021 01:15:42 +0200 Subject: [PATCH 019/320] Switch to HTTPS using a self-signed certificate --- certificates.go | 35 +++++++++++++++++++++++++++++++++-- main.go | 6 ++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/certificates.go b/certificates.go index 680fea8..0c4ffdb 100644 --- a/certificates.go +++ b/certificates.go @@ -1,15 +1,46 @@ package main import ( + "crypto/rand" + "crypto/rsa" "crypto/tls" - "fmt" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "strings" + "time" ) +var fallbackCertKey, _ = rsa.GenerateKey(rand.Reader, 1024) +var fallbackCertSpecification = &x509.Certificate{ + Subject: pkix.Name{ + CommonName: strings.TrimPrefix(string(MainDomainSuffix), "."), + }, + SerialNumber: big.NewInt(0), + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(100, 0, 0), +} +var fallbackCertBytes, _ = x509.CreateCertificate( + rand.Reader, + fallbackCertSpecification, + fallbackCertSpecification, + fallbackCertKey.Public(), + fallbackCertKey, +) +var fallbackCert, _ = tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ + Bytes: fallbackCertBytes, + Type: "CERTIFICATE", +}), pem.EncodeToMemory(&pem.Block{ + Bytes: x509.MarshalPKCS1PrivateKey(fallbackCertKey), + Type: "RSA PRIVATE KEY", +})) + // tlsConfig contains the configuration for generating, serving and cleaning up Let's Encrypt certificates. var tlsConfig = &tls.Config{ GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { // TODO: check DNS name & get certificate from Let's Encrypt - return nil, fmt.Errorf("NYI") + return &fallbackCert, nil }, PreferServerCipherSuites: true, // TODO: optimize cipher suites, minimum TLS version, etc. diff --git a/main.go b/main.go index 60aad97..23ab970 100644 --- a/main.go +++ b/main.go @@ -80,7 +80,7 @@ func main() { // Use HOST and PORT environment variables to determine listening address address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "80")) - fmt.Printf("Listening on http://%s\n", address) + fmt.Printf("Listening on https://%s\n", address) // Enable compression by wrapping the handler() method with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) @@ -91,9 +91,7 @@ func main() { fmt.Printf("Couldn't create listener: %s\n", err) os.Exit(1) } - if envOr("LETS_ENCRYPT", "0") == "1" { - tls.NewListener(listener, tlsConfig) - } + listener = tls.NewListener(listener, tlsConfig) // Start the web server err = (&fasthttp.Server{ From 675e56ee981ceda101571544cb33d96fddf996e5 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 9 Jul 2021 01:16:00 +0200 Subject: [PATCH 020/320] Implement domain handling logic Still lots of performance optimization required! --- domains.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++++----- handler.go | 48 +++++++++++++++++++++++++-- 2 files changed, 133 insertions(+), 12 deletions(-) diff --git a/domains.go b/domains.go index c244e40..1aa3282 100644 --- a/domains.go +++ b/domains.go @@ -1,15 +1,94 @@ package main -import "github.com/valyala/fasthttp" +import ( + "github.com/OrlovEvgeny/go-mcache" + "github.com/valyala/fasthttp" + "net" + "strings" + "time" +) -// getTargetFromDNS searches for CNAME entries on the request domain, optionally with a "www." prefix, and checks if -// the domain is included in the repository's "domains.txt" file. If everything is fine, it returns the target data. -// TODO: use TXT records with A/AAAA/ALIAS -func getTargetFromDNS(ctx *fasthttp.RequestCtx) (targetOwner, targetRepo, targetBranch, targetPath string) { - // TODO: read CNAME record for host and "www.{host}" to get those values - // TODO: check domains.txt +// DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. +var DnsLookupCacheTimeout = 15*time.Minute +// dnsLookupCache stores DNS lookups for custom domains +var dnsLookupCache = mcache.New() + +// getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix, and checks if +// the domain equals the repository's ".canonical-domain" file. If everything is fine, it returns the target data. +func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch string) { + // Get CNAME or TXT + var cname string + var err error + if cachedName, ok := dnsLookupCache.Get(domain); ok { + cname = cachedName.(string) + } else { + cname, err = net.LookupCNAME(domain) + cname = strings.TrimSuffix(cname, ".") + if err != nil || !strings.HasSuffix(cname, string(MainDomainSuffix)) { + cname = "" + names, err := net.LookupTXT(domain) + if err == nil { + for _, name := range names { + name = strings.TrimSuffix(name, ".") + if strings.HasSuffix(name, string(MainDomainSuffix)) { + cname = name + break + } + } + } + } + _ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout) + } + if cname == "" { + return + } + cnameParts := strings.Split(strings.TrimSuffix(cname, string(MainDomainSuffix)), ".") + targetOwner = cnameParts[len(cnameParts)-1] + if len(cnameParts) > 1 { + targetRepo = cnameParts[len(cnameParts)-1] + } + if len(cnameParts) > 2 { + targetBranch = cnameParts[len(cnameParts)-2] + } + if targetRepo == "" { + targetRepo = "pages" + } + if targetBranch == "" && targetRepo != "pages" { + targetBranch = "pages" + } + // if targetBranch is still empty, the caller must find the default branch return } -// TODO: cache domains.txt for 15 minutes -// TODO: canonical domains - redirect to first domain if domains.txt exists, also make sure owner.codeberg.page/pages/... redirects to /... + +// CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. +var CanonicalDomainCacheTimeout = 15*time.Minute +// canonicalDomainCache stores canonical domains +var canonicalDomainCache = mcache.New() + +// checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonicalDomain string) { + // Check if the canonical domain matches + req := fasthttp.AcquireRequest() + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.canonical-domain") + res := fasthttp.AcquireResponse() + if cachedValue, ok := canonicalDomainCache.Get(string(req.RequestURI())); ok { + canonicalDomain = cachedValue.(string) + } else { + err := upstreamClient.Do(req, res) + if err == nil && res.StatusCode() == fasthttp.StatusOK { + canonicalDomain = strings.TrimSpace(string(res.Body())) + if strings.Contains(canonicalDomain, "/") { + canonicalDomain = "" + } + } + if canonicalDomain == "" { + canonicalDomain = targetOwner + string(MainDomainSuffix) + if targetRepo != "" && targetRepo != "pages" { + canonicalDomain += "/" + targetRepo + } + } + _ = canonicalDomainCache.Set(string(req.RequestURI()), canonicalDomain, CanonicalDomainCacheTimeout) + } + return +} diff --git a/handler.go b/handler.go index 7deae03..cb368fb 100644 --- a/handler.go +++ b/handler.go @@ -98,6 +98,19 @@ func handler(ctx *fasthttp.RequestCtx) { // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. var tryUpstream = func() { + // check if a canonical domain exists on a request on MainDomain + if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { + canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) + if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(MainDomainSuffix)) { + canonicalPath := string(ctx.RequestURI()) + if targetRepo != "pages" { + canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + } + ctx.Redirect("https://" + canonicalDomain + canonicalPath, fasthttp.StatusTemporaryRedirect) + return + } + } + // Try to request the file from the Gitea API if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { returnErrorPage(ctx, ctx.Response.StatusCode()) @@ -151,7 +164,7 @@ func handler(ctx *fasthttp.RequestCtx) { if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { if targetRepo == "pages" { // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... - ctx.Redirect("/" + strings.Join(pathElements[1:], "/"), fasthttp.StatusMovedPermanently) + ctx.Redirect("/" + strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) return } @@ -196,12 +209,41 @@ func handler(ctx *fasthttp.RequestCtx) { return } else { // Serve pages from external domains - - targetOwner, targetRepo, targetBranch, targetPath = getTargetFromDNS(ctx) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(string(ctx.Request.Host())) if targetOwner == "" { ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) return } + + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + canonicalLink := "" + if strings.HasPrefix(pathElements[0], "@") { + targetBranch = pathElements[0][1:] + pathElements = pathElements[1:] + canonicalLink = "/%p" + } + + // Try to use the given repo on the given branch or the default branch + if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { + canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) + if canonicalDomain != string(ctx.Request.Host()) { + // only redirect if + targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0]) + if targetOwner != "" { + ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) + return + } else { + ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) + return + } + } + + tryUpstream() + return + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } } } From 0602811709d04006d8373d0cace68676da67a333 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Tue, 13 Jul 2021 10:28:06 +0200 Subject: [PATCH 021/320] Add timing tests & fix caching --- debug-stepper/stepper.go | 68 ++++++++++++++++++++++++++++ domains.go | 12 +++-- handler.go | 98 ++++++++++++++++++++++++++++++---------- handler_test.go | 17 ++++++- 4 files changed, 166 insertions(+), 29 deletions(-) create mode 100644 debug-stepper/stepper.go diff --git a/debug-stepper/stepper.go b/debug-stepper/stepper.go new file mode 100644 index 0000000..0fd0b6f --- /dev/null +++ b/debug-stepper/stepper.go @@ -0,0 +1,68 @@ +package debug_stepper + +import ( + "fmt" + "os" + "strings" + "time" +) + +var Enabled = strings.HasSuffix(os.Args[0], ".test") || os.Getenv("DEBUG") == "1" + +var Logger = func(s string, i ...interface{}) { + fmt.Printf(s, i...) +} + +type Stepper struct { + Name string + Start time.Time + LastStep time.Time + Completion time.Time +} + +func Start(name string) *Stepper { + if !Enabled { + return nil + } + t := time.Now() + Logger("%s: started at %s\n", name, t.Format(time.RFC3339)) + return &Stepper{ + Name: name, + Start: t, + LastStep: t, + } +} + +func (s *Stepper) Debug(text string) { + if !Enabled { + return + } + t := time.Now() + Logger("%s: %s (at %s, %s since last step, %s since start)\n", s.Name, text, t.Format(time.RFC3339), t.Sub(s.LastStep).String(), t.Sub(s.Start).String()) +} + +func (s *Stepper) Step(description string) { + if !Enabled { + return + } + if s.Completion != (time.Time{}) { + Logger("%s: already completed all tasks.\n") + return + } + t := time.Now() + Logger("%s: completed %s at %s (%s)\n", s.Name, description, t.Format(time.RFC3339), t.Sub(s.LastStep).String()) + s.LastStep = t +} + +func (s *Stepper) Complete() { + if !Enabled { + return + } + if s.Completion != (time.Time{}) { + Logger("%s: already completed all tasks.\n") + return + } + t := time.Now() + Logger("%s: completed all tasks at %s (%s since last step; total time: %s)\n", s.Name, t.Format(time.RFC3339), t.Sub(s.LastStep).String(), t.Sub(s.Start).String()) + s.Completion = t +} diff --git a/domains.go b/domains.go index 1aa3282..d692d3e 100644 --- a/domains.go +++ b/domains.go @@ -26,6 +26,7 @@ func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch stri cname = strings.TrimSuffix(cname, ".") if err != nil || !strings.HasSuffix(cname, string(MainDomainSuffix)) { cname = "" + // TODO: check if the A record matches! names, err := net.LookupTXT(domain) if err == nil { for _, name := range names { @@ -69,12 +70,13 @@ var canonicalDomainCache = mcache.New() // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonicalDomain string) { // Check if the canonical domain matches - req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.canonical-domain") - res := fasthttp.AcquireResponse() - if cachedValue, ok := canonicalDomainCache.Get(string(req.RequestURI())); ok { + if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { canonicalDomain = cachedValue.(string) } else { + req := fasthttp.AcquireRequest() + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.canonical-domain") + res := fasthttp.AcquireResponse() + err := upstreamClient.Do(req, res) if err == nil && res.StatusCode() == fasthttp.StatusOK { canonicalDomain = strings.TrimSpace(string(res.Body())) @@ -88,7 +90,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonic canonicalDomain += "/" + targetRepo } } - _ = canonicalDomainCache.Set(string(req.RequestURI()), canonicalDomain, CanonicalDomainCacheTimeout) + _ = canonicalDomainCache.Set(targetOwner + "/" + targetRepo + "/" + targetBranch, canonicalDomain, CanonicalDomainCacheTimeout) } return } diff --git a/handler.go b/handler.go index cb368fb..30ef031 100644 --- a/handler.go +++ b/handler.go @@ -2,6 +2,7 @@ package main import ( "bytes" + debug_stepper "codeberg.org/codeberg/pages/debug-stepper" "fmt" "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" @@ -16,6 +17,9 @@ import ( // handler handles a single HTTP request to the web server. func handler(ctx *fasthttp.RequestCtx) { + s := debug_stepper.Start("handler") + defer s.Complete() + ctx.Response.Header.Set("Server", "Codeberg Pages") // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin @@ -117,8 +121,11 @@ func handler(ctx *fasthttp.RequestCtx) { } } + s.Step("preparations") + if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { // Serve raw content from RawDomain + s.Debug("raw domain") targetOptions.TryIndexPages = false targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} @@ -135,24 +142,30 @@ func handler(ctx *fasthttp.RequestCtx) { // raw.codeberg.page/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { + s.Step("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { + s.Step("tryBranch, now trying upstream") tryUpstream() return } + s.Debug("missing branch") returnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { + s.Step("raw domain preparations, now trying with default branch") tryBranch(targetRepo, "", pathElements[2:], string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) + s.Step("tryBranch, now trying upstream") tryUpstream() return } } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix + s.Debug("main domain suffix") pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) @@ -168,9 +181,11 @@ func handler(ctx *fasthttp.RequestCtx) { return } + s.Step("main domain preparations, now trying with specified repo & branch") if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { + s.Step("tryBranch, now trying upstream") tryUpstream() } else { returnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -181,7 +196,9 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a branch for the "pages" repo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { + s.Step("main domain preparations, now trying with specified branch") if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { + s.Step("tryBranch, now trying upstream") tryUpstream() } else { returnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -192,14 +209,18 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a repo with a "pages" branch // example.codeberg.page/myrepo/index.html // example.codeberg.page/pages/... is not allowed here. + s.Step("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { + s.Step("tryBranch, now trying upstream") tryUpstream() return } // Try to use the "pages" repo on its default branch // example.codeberg.page/index.html + s.Step("main domain preparations, now trying with default repo/branch") if tryBranch("pages", "", pathElements, "") { + s.Step("tryBranch, now trying upstream") tryUpstream() return } @@ -224,10 +245,11 @@ func handler(ctx *fasthttp.RequestCtx) { } // Try to use the given repo on the given branch or the default branch + s.Step("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) if canonicalDomain != string(ctx.Request.Host()) { - // only redirect if + // only redirect if the target is also a codeberg page! targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0]) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) @@ -238,6 +260,7 @@ func handler(ctx *fasthttp.RequestCtx) { } } + s.Step("tryBranch, now trying upstream") tryUpstream() return } else { @@ -255,8 +278,12 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+fasthttp.StatusMessage(code)))) } -// BranchCacheTimeout specifies the timeout for the branch timestamp cache. -var BranchCacheTimeout = 60*time.Second +// BranchExistanceCacheTimeout specifies the timeout for the default branch cache. It can be quite long. +var DefaultBranchCacheTimeout = 1*time.Hour +// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter +// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be +// picked up faster, while still allowing the content to be cached longer if nothing changes. +var BranchExistanceCacheTimeout = 5*time.Minute // branchTimestampCache stores branch timestamps for faster cache checking var branchTimestampCache = mcache.New() type branchTimestamp struct { @@ -264,9 +291,11 @@ type branchTimestamp struct { timestamp time.Time } -// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this shorter -// than BranchCacheTimeout when running out of memory. -var FileCacheTimeout = 60*time.Second +// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending +// on your available memory. +var FileCacheTimeout = 60*time.Minute +// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. +var FileCacheSizeLimit = 1024 * 1024 // fileResponseCache stores responses from the Gitea server var fileResponseCache = mcache.New() type fileResponse struct { @@ -279,27 +308,32 @@ type fileResponse struct { // (or an empty time.Time if the branch doesn't exist) func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { + if result == nil { + return nil + } return result.(*branchTimestamp) } result := &branchTimestamp{} result.branch = branch if branch == "" { + // Get default branch var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, BranchCacheTimeout) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 5 * time.Second) if err != nil || status != 200 { + _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, nil, DefaultBranchCacheTimeout) return nil } result.branch = fastjson.GetString(body, "default_branch") } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, BranchCacheTimeout) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 5 * time.Second) if err != nil || status != 200 { return nil } result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, result, 15 * time.Second) + _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, result, BranchExistanceCacheTimeout) return result } @@ -312,6 +346,9 @@ var upstreamClient = fasthttp.Client{ // upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (final bool) { + s := debug_stepper.Start("upstream") + defer s.Complete() + if options.ForbiddenMimeTypes == nil { options.ForbiddenMimeTypes = map[string]struct{}{} } @@ -340,23 +377,27 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t return true } } + s.Step("preparations") // Make a GET request to the upstream URL - req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath) - res := fasthttp.AcquireResponse() - isCached := false + uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath + var req *fasthttp.Request + var res *fasthttp.Response var cachedResponse fileResponse var err error - if cachedValue, ok := fileResponseCache.Get(string(req.RequestURI())); ok { - isCached = true + if cachedValue, ok := fileResponseCache.Get(uri); ok { cachedResponse = cachedValue.(fileResponse) } else { + req = fasthttp.AcquireRequest() + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri) + res = fasthttp.AcquireResponse() + res.SetBodyStream(&strings.Reader{}, -1) err = upstreamClient.Do(req, res) } + s.Step("acquisition") // Handle errors - if (isCached && !cachedResponse.exists) || res.StatusCode() == fasthttp.StatusNotFound { + if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { if options.TryIndexPages { // copy the options struct & try if an index page exists optionsForIndexPages := *options @@ -364,20 +405,23 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range IndexPages { if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { + _ = fileResponseCache.Set(uri, fileResponse{ + exists: false, + }, FileCacheTimeout) return true } } } ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - if !isCached { + if res != nil { // Update cache if the request is fresh - _ = fileResponseCache.Set(string(req.RequestURI()), fileResponse{ + _ = fileResponseCache.Set(uri, fileResponse{ exists: false, }, FileCacheTimeout) } return false } - if !isCached && (err != nil || res.StatusCode() != fasthttp.StatusOK) { + if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) returnErrorPage(ctx, fasthttp.StatusInternalServerError) return true @@ -389,6 +433,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) return true } + s.Step("error handling") // Set the MIME type mimeType := mime.TypeByExtension(path.Ext(targetPath)) @@ -406,10 +451,16 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Response.SetStatusCode(fasthttp.StatusOK) ctx.Response.Header.SetLastModified(options.BranchTimestamp) + s.Step("response preparations") + // Write the response body to the original request var cacheBodyWriter bytes.Buffer - if !isCached { - err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + if res != nil { + if res.Header.ContentLength() > FileCacheSizeLimit { + err = res.BodyWriteTo(ctx.Response.BodyWriter()) + } else { + err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + } } else { _, err = ctx.Write(cachedResponse.body) } @@ -418,12 +469,13 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t returnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } + s.Step("response") - if !isCached { + if res != nil { cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(string(req.RequestURI()), cachedResponse, FileCacheTimeout) + _ = fileResponseCache.Set(uri, cachedResponse, FileCacheTimeout) } return true diff --git a/handler_test.go b/handler_test.go index 3a6d4b2..e7f51e7 100644 --- a/handler_test.go +++ b/handler_test.go @@ -12,7 +12,7 @@ func TestHandlerPerformance(t *testing.T) { Request: *fasthttp.AcquireRequest(), Response: *fasthttp.AcquireResponse(), } - ctx.Request.SetRequestURI("http://mondstern.codeberg:8080/") + ctx.Request.SetRequestURI("http://mondstern.codeberg.page/") fmt.Printf("Start: %v\n", time.Now()) start := time.Now() handler(ctx) @@ -36,4 +36,19 @@ func TestHandlerPerformance(t *testing.T) { } else { t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) } + + + ctx.Response.Reset() + ctx.Response.ResetBody() + ctx.Request.SetRequestURI("http://example.momar.xyz/") + fmt.Printf("Start: %v\n", time.Now()) + start = time.Now() + handler(ctx) + end = time.Now() + fmt.Printf("Done: %v\n", time.Now()) + if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 1 { + t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) + } else { + t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + } } From 7c70be21d7ead7a03e237b709d71f4b73540e87c Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Tue, 13 Jul 2021 10:28:36 +0200 Subject: [PATCH 022/320] Add HSTS & cipher suites, handle fallback cert errors & change default port to 443 --- certificates.go | 102 +++++++++++++++++++++++++++++++++++------------- handler.go | 11 +++++- main.go | 2 +- 3 files changed, 86 insertions(+), 29 deletions(-) diff --git a/certificates.go b/certificates.go index 0c4ffdb..8f73210 100644 --- a/certificates.go +++ b/certificates.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "crypto/rand" "crypto/rsa" "crypto/tls" @@ -12,38 +13,85 @@ import ( "time" ) -var fallbackCertKey, _ = rsa.GenerateKey(rand.Reader, 1024) -var fallbackCertSpecification = &x509.Certificate{ - Subject: pkix.Name{ - CommonName: strings.TrimPrefix(string(MainDomainSuffix), "."), - }, - SerialNumber: big.NewInt(0), - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(100, 0, 0), -} -var fallbackCertBytes, _ = x509.CreateCertificate( - rand.Reader, - fallbackCertSpecification, - fallbackCertSpecification, - fallbackCertKey.Public(), - fallbackCertKey, -) -var fallbackCert, _ = tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ - Bytes: fallbackCertBytes, - Type: "CERTIFICATE", -}), pem.EncodeToMemory(&pem.Block{ - Bytes: x509.MarshalPKCS1PrivateKey(fallbackCertKey), - Type: "RSA PRIVATE KEY", -})) - // tlsConfig contains the configuration for generating, serving and cleaning up Let's Encrypt certificates. var tlsConfig = &tls.Config{ GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { // TODO: check DNS name & get certificate from Let's Encrypt - return &fallbackCert, nil + return FallbackCertificate(), nil }, PreferServerCipherSuites: true, - // TODO: optimize cipher suites, minimum TLS version, etc. + + // generated 2021-07-13, Mozilla Guideline v5.6, Go 1.14.4, intermediate configuration + // https://ssl-config.mozilla.org/#server=go&version=1.14.4&config=intermediate&guideline=5.6 + MinVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, } -// TODO: HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain +// GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty +// string for custom domains. +func GetHSTSHeader(host []byte) string { + if bytes.HasSuffix(host, MainDomainSuffix) || bytes.Equal(host, RawDomain) { + return "max-age=63072000; includeSubdomains; preload" + } else { + return "" + } +} + +var fallbackCertificate *tls.Certificate +// FallbackCertificate generates a new self-signed TLS certificate on demand. +func FallbackCertificate() *tls.Certificate { + if fallbackCertificate != nil { + return fallbackCertificate + } + + fallbackSerial, err := rand.Int(rand.Reader, (&big.Int{}).Lsh(big.NewInt(1), 159)) + if err != nil { + panic(err) + } + + fallbackCertKey, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + panic(err) + } + + fallbackCertSpecification := &x509.Certificate{ + Subject: pkix.Name{ + CommonName: strings.TrimPrefix(string(MainDomainSuffix), "."), + }, + SerialNumber: fallbackSerial, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(100, 0, 0), + } + + fallbackCertBytes, err := x509.CreateCertificate( + rand.Reader, + fallbackCertSpecification, + fallbackCertSpecification, + fallbackCertKey.Public(), + fallbackCertKey, + ) + if err != nil { + panic(err) + } + + fallbackCert, err := tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ + Bytes: fallbackCertBytes, + Type: "CERTIFICATE", + }), pem.EncodeToMemory(&pem.Block{ + Bytes: x509.MarshalPKCS1PrivateKey(fallbackCertKey), + Type: "RSA PRIVATE KEY", + })) + if err != nil { + panic(err) + } + + fallbackCertificate = &fallbackCert + return fallbackCertificate +} diff --git a/handler.go b/handler.go index 30ef031..909725f 100644 --- a/handler.go +++ b/handler.go @@ -28,6 +28,11 @@ func handler(ctx *fasthttp.RequestCtx) { // Enable caching, but require revalidation to reduce confusion ctx.Response.Header.Set("Cache-Control", "must-revalidate") + // Add HSTS for RawDomain and MainDomainSuffix + if hsts := GetHSTSHeader(ctx.Host()); hsts != "" { + ctx.Response.Header.Set("Strict-Transport-Security", hsts) + } + // Block all methods not required for static pages if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") @@ -275,7 +280,11 @@ func handler(ctx *fasthttp.RequestCtx) { func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetStatusCode(code) ctx.Response.Header.SetContentType("text/html; charset=utf-8") - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+fasthttp.StatusMessage(code)))) + message := fasthttp.StatusMessage(code) + if code == fasthttp.StatusFailedDependency { + message += " - owner, repo or branch doesn't exist" + } + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) } // BranchExistanceCacheTimeout specifies the timeout for the default branch cache. It can be quite long. diff --git a/main.go b/main.go index 23ab970..cc0b43f 100644 --- a/main.go +++ b/main.go @@ -79,7 +79,7 @@ func main() { GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) // Use HOST and PORT environment variables to determine listening address - address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "80")) + address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "443")) fmt.Printf("Listening on https://%s\n", address) // Enable compression by wrapping the handler() method with the compression function provided by FastHTTP From 449402308669b8d9bef70e84449f8f8ba056f811 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Tue, 13 Jul 2021 15:45:28 +0200 Subject: [PATCH 023/320] Implement simple version of automatic certificate issuance --- .gitignore | 1 + README.md | 11 + certificates.go | 271 ++++++++++++++++- domains.go | 6 +- go.mod | 2 + go.sum | 757 ++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 36 +-- 7 files changed, 1061 insertions(+), 23 deletions(-) create mode 100644 README.md diff --git a/.gitignore b/.gitignore index 1062418..983b8b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ *.iml +key-database.pogreb/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..10445b9 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +## Environment + +- `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. +- `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. +- `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. +- `REDIRECT_BROKEN_DNS` (default: "https://docs.codeberg.org/pages/custom-domains/"): info page for setting up DNS, shown for invalid DNS setups. +- `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. +- `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): Set this to "https://acme-staging-v02.api.letsencrypt.org/directory" to use the staging API of Let's Encrypt instead. +- `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. +- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. + See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. diff --git a/certificates.go b/certificates.go index 8f73210..faccf7d 100644 --- a/certificates.go +++ b/certificates.go @@ -2,24 +2,169 @@ package main import ( "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "errors" + "github.com/OrlovEvgeny/go-mcache" + "github.com/akrylysov/pogreb/fs" + "github.com/go-acme/lego/v4/certificate" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" + "github.com/go-acme/lego/v4/providers/dns" + "log" "math/big" + "os" "strings" "time" + + "github.com/akrylysov/pogreb" + + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/registration" ) // tlsConfig contains the configuration for generating, serving and cleaning up Let's Encrypt certificates. var tlsConfig = &tls.Config{ + // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - // TODO: check DNS name & get certificate from Let's Encrypt - return FallbackCertificate(), nil + if os.Getenv("ACME_ACCEPT_TERMS") != "true" { + return FallbackCertificate(), nil + } + + sni := strings.ToLower(strings.TrimSpace(info.ServerName)) + sniBytes := []byte(sni) + if len(sni) < 1 { + return nil, errors.New("missing sni") + } + + if info.SupportedProtos != nil { + for _, proto := range info.SupportedProtos { + if proto == tlsalpn01.ACMETLS1Protocol { + challenge, ok := challengeCache.Get(sni) + if !ok { + return nil, errors.New("no challenge for this domain") + } + cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) + if err != nil { + return nil, err + } + return cert, nil + } + } + } + + targetOwner := "" + if bytes.HasSuffix(sniBytes, MainDomainSuffix) { + // deliver default certificate for the main domain (*.codeberg.page) + sniBytes = MainDomainSuffix + sni = string(sniBytes) + } else { + var targetRepo, targetBranch string + targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni) + if targetOwner == "" { + // DNS not set up, return a self-signed certificate to redirect to the docs + return FallbackCertificate(), nil + } + + // TODO: use .domains file to list all domains, to keep users from getting rate-limited + _, _ = targetRepo, targetBranch + /*canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) + if sni != canonicalDomain { + return FallbackCertificate(), nil + }*/ + } + + // limit users to 1 certificate per week + + var cert, key []byte + if tlsCertificate, ok := keyCache.Get(sni); ok { + // we can use an existing certificate object + return tlsCertificate.(*tls.Certificate), nil + } else if ok, err := keyDatabase.Has(sniBytes); err != nil { + // key database is not working + panic(err) + } else if ok { + // parse certificate from database + + cert, err = keyDatabase.Get(sniBytes) + if err != nil { + // key database is not working + panic(err) + } + key, err = keyDatabase.Get(append(sniBytes, '/', 'k', 'e', 'y')) + if err != nil { + // key database is not working or key doesn't exist + panic(err) + } + } else { + // request a new certificate + + // TODO: rate-limit certificates per owner + // LE Rate Limits: + // - 300 new orders per account per 3 hours + // - 20 requests per second + // - 10 Accounts per IP per 3 hours + + if bytes.Equal(sniBytes, MainDomainSuffix) { + return nil, errors.New("won't request certificate for main domain, something really bad has happened") + } + + log.Printf("Requesting new certificate for %s", sni) + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + key = x509.MarshalPKCS1PrivateKey(privateKey) + res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ + Domains: []string{sni}, + PrivateKey: key, + Bundle: true, + MustStaple: true, + }) + if err != nil { + return nil, err + } + log.Printf("Obtained certificate for %s", sni) + err = keyDatabase.Put(append(sniBytes, '/', 'k', 'e', 'y'), key) + if err != nil { + return nil, err + } + err = keyDatabase.Put(sniBytes, res.Certificate) + if err != nil { + _ = keyDatabase.Delete(append(sniBytes, '/', 'k', 'e', 'y')) + return nil, err + } + cert = res.Certificate + } + tlsCertificate, err := tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ + Bytes: cert, + Type: "CERTIFICATE", + }), pem.EncodeToMemory(&pem.Block{ + Bytes: key, + Type: "RSA PRIVATE KEY", + })) + if err != nil { + panic(err) + } + + err = keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) + if err != nil { + panic(err) + } + return &tlsCertificate, nil }, PreferServerCipherSuites: true, + NextProtos: []string{ + tlsalpn01.ACMETLS1Protocol, + }, // generated 2021-07-13, Mozilla Guideline v5.6, Go 1.14.4, intermediate configuration // https://ssl-config.mozilla.org/#server=go&version=1.14.4&config=intermediate&guideline=5.6 @@ -44,6 +189,10 @@ func GetHSTSHeader(host []byte) string { } } +var challengeCache = mcache.New() +var keyCache = mcache.New() +var keyDatabase *pogreb.DB + var fallbackCertificate *tls.Certificate // FallbackCertificate generates a new self-signed TLS certificate on demand. func FallbackCertificate() *tls.Certificate { @@ -95,3 +244,121 @@ func FallbackCertificate() *tls.Certificate { fallbackCertificate = &fallbackCert return fallbackCertificate } + +type AcmeAccount struct { + Email string + Registration *registration.Resource + key crypto.PrivateKey +} +func (u *AcmeAccount) GetEmail() string { + return u.Email +} +func (u AcmeAccount) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { + return u.key +} +var acmeClient *lego.Client + +type AcmeTLSChallengeProvider struct{} +var _ challenge.Provider = AcmeTLSChallengeProvider{} +func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { + return challengeCache.Set(domain, keyAuth, 1*time.Hour) +} +func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { + challengeCache.Remove(domain) + return nil +} + +func init() { + FallbackCertificate() + + var err error + keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ + BackgroundSyncInterval: 30 * time.Second, + BackgroundCompactionInterval: 6 * time.Hour, + FileSystem: fs.OSMMap, + }) + if err != nil { + panic(err) + } + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + myUser := AcmeAccount{ + Email: "", + key: privateKey, + } + config := lego.NewConfig(&myUser) + config.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") + config.Certificate.KeyType = certcrypto.RSA2048 + acmeClient, err = lego.NewClient(config) + if err != nil { + panic(err) + } + err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + if err != nil { + panic(err) + } + + // accept terms + if os.Getenv("ACME_ACCEPT_TERMS") == "true" { + reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if err != nil { + panic(err) + } + myUser.Registration = reg + } else { + log.Printf("Warning: not using ACME certificates as ACME_ACCEPT_TERMS is false!") + } + + // generate certificate for main domain + if os.Getenv("ACME_ACCEPT_TERMS") != "true" || os.Getenv("DNS_PROVIDER") == "" { + err = keyCache.Set(string(MainDomainSuffix), FallbackCertificate(), mcache.TTL_FOREVER) + if err != nil { + panic(err) + } + } else { + log.Printf("Requesting new certificate for *%s", MainDomainSuffix) + dnsAcmeClient, err := lego.NewClient(config) + if err != nil { + panic(err) + } + provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) + if err != nil { + panic(err) + } + err = dnsAcmeClient.Challenge.SetDNS01Provider(provider) + if err != nil { + panic(err) + } + mainPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + mainKey := x509.MarshalPKCS1PrivateKey(mainPrivateKey) + res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ + Domains: []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, + PrivateKey: mainKey, + Bundle: true, + MustStaple: true, + }) + if err != nil { + panic(err) + } + err = keyDatabase.Put(append(MainDomainSuffix, '/', 'k', 'e', 'y'), mainKey) + if err != nil { + panic(err) + } + err = keyDatabase.Put(MainDomainSuffix, res.Certificate) + if err != nil { + _ = keyDatabase.Delete(append(MainDomainSuffix, '/', 'k', 'e', 'y')) + panic(err) + } + } +} + +// TODO: renew & revoke diff --git a/domains.go b/domains.go index d692d3e..5b66551 100644 --- a/domains.go +++ b/domains.go @@ -13,8 +13,8 @@ var DnsLookupCacheTimeout = 15*time.Minute // dnsLookupCache stores DNS lookups for custom domains var dnsLookupCache = mcache.New() -// getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix, and checks if -// the domain equals the repository's ".canonical-domain" file. If everything is fine, it returns the target data. +// getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. +// If everything is fine, it returns the target data. func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string @@ -74,7 +74,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonic canonicalDomain = cachedValue.(string) } else { req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.canonical-domain") + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains") res := fasthttp.AcquireResponse() err := upstreamClient.Do(req, res) diff --git a/go.mod b/go.mod index b5102c7..ba46849 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,9 @@ go 1.16 require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a + github.com/akrylysov/pogreb v0.10.1 github.com/andybalholm/brotli v1.0.3 // indirect + github.com/go-acme/lego/v4 v4.4.0 github.com/klauspost/compress v1.13.1 // indirect github.com/valyala/fasthttp v1.28.0 github.com/valyala/fastjson v1.6.3 diff --git a/go.sum b/go.sum index 61d068d..02603a8 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,783 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= +github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0 h1:YgO/vSnJEc76NLw2ecIXvXa8bDWiqf1pOJzARAoZsYU= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0 h1:YTtBrcb6mhA+PoSW8WxFDoIIyjp13XqJeX80ssQtri4= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= +github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= +github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8bwpXIePbGLW4z/AmUFGH5A= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= +github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= +github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0 h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= +github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= +github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.976 h1:I9fs4eZbZqimF3TstEqEwK66R2b7QKd6D6OCxibSD60= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.976/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.37.27 h1:zh+56ktUEUjA5lMsolBnjVDaYOWadwCwx1DMF7tFM8Y= +github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= +github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= +github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= +github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= +github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.3.11 h1:Nd3tDQfqgquLmCzyRONHzs5SJEwPPoQcFZxT8MKt1Hs= +github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= +github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.63.0 h1:0doY8VW/ckRIMTmOw4E1vwqo+bhtjDzvh1pU2ZteFGA= +github.com/dnsimple/dnsimple-go v0.63.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/exoscale/egoscale v0.46.0 h1:i1Ut7oqFaOV8opNP9CHcwTnHryIAcTvL3pwZusQEGrA= +github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc= +github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0= +github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= +github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= +github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= +github.com/gophercloud/gophercloud v0.16.0 h1:sWjPfypuzxRxjVbk3/MsU4H8jS0NNlyauZtIUl78BPU= +github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= +github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15Kfovd8MTZrcana+UlQqNbOif8dLpA0= +github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= +github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= +github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g= +github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= +github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk= +github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= +github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= +github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/linode/linodego v0.25.3 h1:OKUepYBitakHsnI75maYefrkBtOz6CGt/4o4CKnMlnM= +github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= +github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= +github.com/liquidweb/go-lwApi v0.0.5 h1:CT4cdXzJXmo0bon298kS7NeSk+Gt8/UHpWBBol1NGCA= +github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= +github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM= +github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= +github.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOEYGSKrk4= +github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= +github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y= +github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= +github.com/nrdcg/desec v0.5.0 h1:foL7hqivYOMlv0qDhHXJtuuEXkqf0wW9EQMqyrt228g= +github.com/nrdcg/desec v0.5.0/go.mod h1:2ejvMazkav1VdDbv2HeQO7w+Ta1CGHqzQr27ZBYTuEQ= +github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= +github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/goinwx v0.8.1 h1:20EQ/JaGFnSKwiDH2JzjIpicffl3cPk6imJBDqVBVtU= +github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c= +github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= +github.com/nrdcg/porkbun v0.1.1 h1:gxVzQYfFUGXhnBax/aVugoE3OIBAdHgrJgyMPyY5Sjo= +github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= +github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= +github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= +github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= +github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= +github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE= +github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= +github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE= +github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= +github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ= +github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 h1:d9qaMM+ODpCq+9We41//fu/sHsTnXcrqd1en3x+GKy4= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ns1/ns1-go.v2 v2.4.4 h1:xV/rJE2m8p3IncnwMEFrv36K4NDnRXu4CoIkgq+AZek= +gopkg.in/ns1/ns1-go.v2 v2.4.4/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/main.go b/main.go index cc0b43f..9fda1bc 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/tls" "fmt" + "log" "net" "os" "time" @@ -41,7 +42,7 @@ var GiteaRoot = []byte(envOr("GITEA_ROOT", "https://codeberg.org")) var NotFoundPage []byte // BrokenDNSPage will be shown (with a redirect) when trying to access a domain for which no DNS CNAME record exists. -var BrokenDNSPage = envOr("REDIRECT_BROKEN_DNS", "https://docs.codeberg.org/codeberg-pages/custom-domains/") +var BrokenDNSPage = envOr("REDIRECT_BROKEN_DNS", "https://docs.codeberg.org/pages/custom-domains/") // RawDomain specifies the domain from which raw repository content shall be served in the following format: // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} @@ -49,7 +50,7 @@ var BrokenDNSPage = envOr("REDIRECT_BROKEN_DNS", "https://docs.codeberg.org/code var RawDomain = []byte(envOr("RAW_DOMAIN", "raw.codeberg.org")) // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). -var RawInfoPage = envOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/codeberg-pages/raw-content/") +var RawInfoPage = envOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. var AllowedCorsDomains = [][]byte{ @@ -66,8 +67,6 @@ var BlacklistedPaths = [][]byte{ // IndexPages lists pages that may be considered as index pages for directories. var IndexPages = []string{ "index.html", - "index.htm", - "README.md", } // main sets up and starts the web server. @@ -80,21 +79,12 @@ func main() { // Use HOST and PORT environment variables to determine listening address address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "443")) - fmt.Printf("Listening on https://%s\n", address) + log.Printf("Listening on https://%s", address) // Enable compression by wrapping the handler() method with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) - // Setup listener and TLS - listener, err := net.Listen("tcp", address) - if err != nil { - fmt.Printf("Couldn't create listener: %s\n", err) - os.Exit(1) - } - listener = tls.NewListener(listener, tlsConfig) - - // Start the web server - err = (&fasthttp.Server{ + server := &fasthttp.Server{ Handler: compressedHandler, DisablePreParseMultipartForm: false, MaxRequestBodySize: 0, @@ -103,10 +93,20 @@ func main() { ReadTimeout: 10 * time.Second, Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! MaxConnsPerIP: 100, - }).Serve(listener) + } + //fasthttp2.ConfigureServerAndConfig(server, tlsConfig) + + // Setup listener and TLS + listener, err := net.Listen("tcp", address) if err != nil { - fmt.Printf("Couldn't start server: %s\n", err) - os.Exit(1) + log.Fatalf("Couldn't create listener: %s", err) + } + listener = tls.NewListener(listener, tlsConfig) + + // Start the web server + err = server.Serve(listener) + if err != nil { + log.Fatalf("Couldn't start server: %s", err) } } From f89cdae35958c51cbc39206986ec7b3ed1738f19 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 18 Apr 2021 23:58:11 +0200 Subject: [PATCH 024/320] update dependencies --- Cargo.lock | 24 ++++++++++++------------ LICENSE | 2 +- src/main.rs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa471f0..fc92dad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "clap" -version = "2.33.0" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "ansi_term", "atty", @@ -49,9 +49,9 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" [[package]] name = "hermit-abi" -version = "0.1.11" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] @@ -66,9 +66,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.69" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "strsim" @@ -87,21 +87,21 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", diff --git a/LICENSE b/LICENSE index 86af5a7..f69e94a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 crapStone +Copyright (c) 2021 crapStone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/main.rs b/src/main.rs index a90736b..929e190 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ fn main() { Some("raw") => Box::new(RawController::new(p)), Some("lin") => Box::new(LinController::new(p)), Some("log") => Box::new(LogController::new(p)), - Some(_) | None => panic!(ERROR_MSG), + Some(_) | None => panic!("{}", ERROR_MSG), }; if matches.is_present("list") { @@ -41,7 +41,7 @@ fn main() { } else if matches.is_present("ful") { controller.set_brightness(controller.get_max_brightness()); } else { - panic!(ERROR_MSG); + panic!("{}", ERROR_MSG); } exit(exitcode::OK); From 334b2a917a1f4f5c38b3227c0d18e5b905fbcceb Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 22 Aug 2021 15:41:18 +0200 Subject: [PATCH 025/320] update dependencies --- Cargo.lock | 14 ++++++++------ Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc92dad..0cfd5b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -22,9 +24,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "clap" @@ -49,9 +51,9 @@ checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -66,9 +68,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "strsim" diff --git a/Cargo.toml b/Cargo.toml index 5b39454..033bf70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -clap = "2.33" +clap = "2.33.3" [dependencies] -clap = "2.33" +clap = "2.33.3" exitcode = "1.1.2" From 33f7a5d0df0288b7625f77449134f8cda7078698 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 22 Aug 2021 17:59:30 +0200 Subject: [PATCH 026/320] Commit all current changes before vacation... --- certificates.go | 74 ++++++++++++++++++++++++++++++++++++------------- go.mod | 1 + go.sum | 2 ++ handler.go | 11 ++++---- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/certificates.go b/certificates.go index faccf7d..0c4ec18 100644 --- a/certificates.go +++ b/certificates.go @@ -25,6 +25,7 @@ import ( "time" "github.com/akrylysov/pogreb" + "github.com/reugn/equalizer" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/lego" @@ -107,12 +108,6 @@ var tlsConfig = &tls.Config{ } else { // request a new certificate - // TODO: rate-limit certificates per owner - // LE Rate Limits: - // - 300 new orders per account per 3 hours - // - 20 requests per second - // - 10 Accounts per IP per 3 hours - if bytes.Equal(sniBytes, MainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } @@ -123,6 +118,10 @@ var tlsConfig = &tls.Config{ return nil, err } key = x509.MarshalPKCS1PrivateKey(privateKey) + acmeClient, err := acmeClientFromPool(targetOwner) + if err != nil { + // TODO + } res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: []string{sni}, PrivateKey: key, @@ -259,7 +258,17 @@ func (u AcmeAccount) GetRegistration() *registration.Resource { func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { return u.key } -var acmeClient *lego.Client + +// rate-limit certificates per owner, based on LE Rate Limits: +// - 300 new orders per account per 3 hours +// - 20 requests per second +// - 10 Accounts per IP per 3 hours +var acmeClientPool []*lego.Client +var lastAcmeClient = 0 +var acmeClientRequestLimit = equalizer.NewTokenBucket(10, time.Second) // LE allows 20 requests per second, but we want to give other applications a chancem so we want 10 here at most. +var acmeClientRegistrationLimit = equalizer.NewTokenBucket(5, time.Hour * 3) // LE allows 10 registrations in 3 hours per IP, we want at most 5 of them. +var acmeClientCertificateLimitPerRegistration = []*equalizer.TokenBucket{} +var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} @@ -271,19 +280,31 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { return nil } -func init() { - FallbackCertificate() +func acmeClientFromPool(user string) (*lego.Client, error) { + userLimit, ok := acmeClientCertificateLimitPerUser[user] + if !ok { + // Each Codeberg user can only add 10 new domains per day. + userLimit = equalizer.NewTokenBucket(10, time.Hour * 24) + acmeClientCertificateLimitPerUser[user] = userLimit - var err error - keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ - BackgroundSyncInterval: 30 * time.Second, - BackgroundCompactionInterval: 6 * time.Hour, - FileSystem: fs.OSMMap, - }) - if err != nil { - panic(err) + } + if !userLimit.Ask() { + return nil, errors.New("rate limit exceeded: 10 certificates per user per 24 hours") } + if len(acmeClientPool) < 1 { + acmeClientPool = append(acmeClientPool, newAcmeClient()) + acmeClientCertificateLimitPerRegistration = append(acmeClientCertificateLimitPerRegistration, equalizer.NewTokenBucket(290, time.Hour * 3)) + } + if !acmeClientCertificateLimitPerRegistration[(lastAcmeClient + 1) % len(acmeClientPool)].Ask() { + + } + equalizer.NewTokenBucket(290, time.Hour * 3) // LE allows 300 certificates per account, to be sure to catch it earlier, we limit that to 290. + + // TODO: limit domains by file in repo +} + +func newAcmeClient() *lego.Client { privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { panic(err) @@ -295,7 +316,7 @@ func init() { config := lego.NewConfig(&myUser) config.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") config.Certificate.KeyType = certcrypto.RSA2048 - acmeClient, err = lego.NewClient(config) + acmeClient, err := lego.NewClient(config) if err != nil { panic(err) } @@ -314,6 +335,21 @@ func init() { } else { log.Printf("Warning: not using ACME certificates as ACME_ACCEPT_TERMS is false!") } + return acmeClient +} + +func init() { + FallbackCertificate() + + var err error + keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ + BackgroundSyncInterval: 30 * time.Second, + BackgroundCompactionInterval: 6 * time.Hour, + FileSystem: fs.OSMMap, + }) + if err != nil { + panic(err) + } // generate certificate for main domain if os.Getenv("ACME_ACCEPT_TERMS") != "true" || os.Getenv("DNS_PROVIDER") == "" { @@ -340,7 +376,7 @@ func init() { panic(err) } mainKey := x509.MarshalPKCS1PrivateKey(mainPrivateKey) - res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ + res, err := dnsAcmeClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, PrivateKey: mainKey, Bundle: true, diff --git a/go.mod b/go.mod index ba46849..5920606 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/andybalholm/brotli v1.0.3 // indirect github.com/go-acme/lego/v4 v4.4.0 github.com/klauspost/compress v1.13.1 // indirect + github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/valyala/fasthttp v1.28.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index 02603a8..0180b2f 100644 --- a/go.sum +++ b/go.sum @@ -409,6 +409,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= +github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/handler.go b/handler.go index 909725f..ee52257 100644 --- a/handler.go +++ b/handler.go @@ -91,6 +91,7 @@ func handler(ctx *fasthttp.RequestCtx) { targetRepo = repo targetPath = strings.Trim(strings.Join(path, "/"), "/") targetBranch = branchTimestampResult.branch + targetOptions.BranchTimestamp = branchTimestampResult.timestamp if canonicalLink != "" { @@ -314,7 +315,7 @@ type fileResponse struct { } // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch -// (or an empty time.Time if the branch doesn't exist) +// (or nil if the branch doesn't exist) func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { @@ -394,7 +395,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t var res *fasthttp.Response var cachedResponse fileResponse var err error - if cachedValue, ok := fileResponseCache.Get(uri); ok { + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok { cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() @@ -414,7 +415,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range IndexPages { if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { - _ = fileResponseCache.Set(uri, fileResponse{ + _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) return true @@ -424,7 +425,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Response.SetStatusCode(fasthttp.StatusNotFound) if res != nil { // Update cache if the request is fresh - _ = fileResponseCache.Set(uri, fileResponse{ + _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) } @@ -484,7 +485,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri, cachedResponse, FileCacheTimeout) + _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) } return true From 29799b7c3ae89b54d6996a8ce8e5469b2e4903b3 Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 5 Oct 2021 13:48:34 +0200 Subject: [PATCH 027/320] update dependencies --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0cfd5b3..4b11455 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.100" +version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" [[package]] name = "strsim" @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "vec_map" From 6def9f298c8fb26d086c1a7caf983763fe8f89d1 Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 5 Oct 2021 14:12:22 +0200 Subject: [PATCH 028/320] fix help message --- src/cli.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index fd665e2..44e3c0f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,9 @@ use clap::{App, Arg, ArgGroup, ArgMatches}; pub fn build_cli() -> App<'static, 'static> { - App::new("RS Light") - .version("1.0") - .author("crapStone ") + App::new("lamp") + .version(env!("CARGO_PKG_VERSION")) + .author("crapStone ") .about("Utility to interact with backlight") .arg( Arg::with_name("set") From b5402847642ae1e970b8cfdf92f7ef19437e67e9 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 23 Oct 2021 22:57:56 +0200 Subject: [PATCH 029/320] upgrade to edition 2021, clippy fixes, error fixed --- Cargo.lock | 6 +++--- Cargo.toml | 6 +++--- src/cli.rs | 6 +++++- src/controllers.rs | 6 ++---- src/main.rs | 9 +++++---- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b11455..6c0fe92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,7 +60,7 @@ dependencies = [ [[package]] name = "lamp" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "exitcode", @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.103" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" [[package]] name = "strsim" diff --git a/Cargo.toml b/Cargo.toml index 033bf70..503b0ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "lamp" -version = "0.1.0" -authors = ["crapStone "] -edition = "2018" +version = "0.2.0" +authors = ["crapStone "] +edition = "2021" build = "build.rs" diff --git a/src/cli.rs b/src/cli.rs index 44e3c0f..a64ceca 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,6 +5,7 @@ pub fn build_cli() -> App<'static, 'static> { .version(env!("CARGO_PKG_VERSION")) .author("crapStone ") .about("Utility to interact with backlight") + .global_setting(clap::AppSettings::ArgRequiredElseHelp) .arg( Arg::with_name("set") .short("s") @@ -47,7 +48,10 @@ pub fn build_cli() -> App<'static, 'static> { .long("full") .help("Sets brightness to highest value"), ) - .group(ArgGroup::with_name("brightness_control").args(&["set", "inc", "dec", "get", "zer", "ful"])) + .group( + ArgGroup::with_name("brightness_control") + .args(&["set", "inc", "dec", "get", "zer", "ful"]), + ) .arg( Arg::with_name("list") .short("l") diff --git a/src/controllers.rs b/src/controllers.rs index 79e8342..8626c21 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -4,8 +4,6 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::exit; -use exitcode; - const SYS_PATHS: [&str; 2] = ["/sys/class/backlight", "/sys/class/leds"]; pub trait Controller { @@ -34,7 +32,7 @@ pub struct RawController { impl RawController { pub fn new(path: Box) -> Self { - Self { path: path } + Self { path } } } @@ -185,7 +183,7 @@ pub fn get_controllers() -> (String, HashMap>) { let mut default = None; - for path in SYS_PATHS.iter() { + for path in SYS_PATHS { if Path::new(path).exists() { for name in Path::new(path).read_dir().unwrap() { let name = name.unwrap().path(); diff --git a/src/main.rs b/src/main.rs index 929e190..8059fbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,13 @@ mod controllers; use std::process::exit; -use exitcode; - use controllers::{Controller, LinController, LogController, RawController}; +use crate::cli::build_cli; + fn main() { - let matches = cli::parse_args(); + let app = build_cli(); + let matches = app.get_matches(); let (default_ctrl, ctrls) = controllers::get_controllers(); @@ -41,7 +42,7 @@ fn main() { } else if matches.is_present("ful") { controller.set_brightness(controller.get_max_brightness()); } else { - panic!("{}", ERROR_MSG); + build_cli().print_long_help().unwrap(); } exit(exitcode::OK); From cf5e9e1ac089bf85ce2021b3b6d9873fb86ee427 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 23 Oct 2021 23:05:49 +0200 Subject: [PATCH 030/320] remove unused function --- src/cli.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index a64ceca..213c47c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg, ArgGroup, ArgMatches}; +use clap::{App, Arg, ArgGroup}; pub fn build_cli() -> App<'static, 'static> { App::new("lamp") @@ -78,9 +78,3 @@ log: uses percentage values (0.0 - 1.0) with a logarithmic curve for the actual ), ) } - -/// Creates a argument parser with [clap](../clap/index.html) and returns a `Box` with the -/// [matches](../clap/struct.ArgMatches.html). -pub fn parse_args<'a>() -> Box> { - Box::new(build_cli().get_matches()) -} From dbccffc135d489da468b18256a002fd122f0888a Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 26 Oct 2021 17:31:38 +0200 Subject: [PATCH 031/320] make binary faster and smaller --- Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 503b0ce..9666175 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,9 @@ clap = "2.33.3" [dependencies] clap = "2.33.3" exitcode = "1.1.2" + +[profile.release] +lto = true +panic = "abort" +codegen-units = 1 + From 2aaac2c52b11908bd845835b05bd2db5652a29a8 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 15:30:58 +0100 Subject: [PATCH 032/320] Completely refactor certificates and implement renewal & cleanup --- 404.html | 4 +- README.md | 6 +- certificates.go | 387 +++++++++++++++++++++--------------------------- domains.go | 40 +++-- handler.go | 30 ++-- helpers.go | 21 +++ main.go | 1 - 7 files changed, 242 insertions(+), 247 deletions(-) create mode 100644 helpers.go diff --git a/404.html b/404.html index 3b5265c..fa17a83 100644 --- a/404.html +++ b/404.html @@ -5,7 +5,7 @@ %status - + @@ -29,7 +29,7 @@ Sorry, this page doesn't exist or is inaccessible for other reasons (%status) - + Static pages made easy - Codeberg Pages diff --git a/README.md b/README.md index 10445b9..70d4ebe 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ ## Environment +- `HOST` & `PORT` (default: `[::]` & `443`): listen address. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. - `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. -- `REDIRECT_BROKEN_DNS` (default: "https://docs.codeberg.org/pages/custom-domains/"): info page for setting up DNS, shown for invalid DNS setups. +- `REDIRECT_BROKEN_DNS` (default: https://docs.codeberg.org/pages/custom-domains/): info page for setting up DNS, shown for invalid DNS setups. - `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. -- `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): Set this to "https://acme-staging-v02.api.letsencrypt.org/directory" to use the staging API of Let's Encrypt instead. +- `ACME_API` (default: https://acme.zerossl.com/v2/DV90): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL is used as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt). +- `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. diff --git a/certificates.go b/certificates.go index 0c4ec18..3ba6f9e 100644 --- a/certificates.go +++ b/certificates.go @@ -6,20 +6,17 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" "errors" "github.com/OrlovEvgeny/go-mcache" "github.com/akrylysov/pogreb/fs" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/resolver" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/providers/dns" "log" - "math/big" "os" "strings" "time" @@ -36,10 +33,6 @@ import ( var tlsConfig = &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - if os.Getenv("ACME_ACCEPT_TERMS") != "true" { - return FallbackCertificate(), nil - } - sni := strings.ToLower(strings.TrimSpace(info.ServerName)) sniBytes := []byte(sni) if len(sni) < 1 { @@ -63,7 +56,7 @@ var tlsConfig = &tls.Config{ } targetOwner := "" - if bytes.HasSuffix(sniBytes, MainDomainSuffix) { + if bytes.HasSuffix(sniBytes, MainDomainSuffix) || bytes.Equal(sniBytes, MainDomainSuffix[1:]) { // deliver default certificate for the main domain (*.codeberg.page) sniBytes = MainDomainSuffix sni = string(sniBytes) @@ -71,90 +64,68 @@ var tlsConfig = &tls.Config{ var targetRepo, targetBranch string targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni) if targetOwner == "" { - // DNS not set up, return a self-signed certificate to redirect to the docs - return FallbackCertificate(), nil + // DNS not set up, return main certificate to redirect to the docs + sniBytes = MainDomainSuffix + sni = string(sniBytes) + } else { + _, _ = targetRepo, targetBranch + _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni) + if !valid { + sniBytes = MainDomainSuffix + sni = string(sniBytes) + } } - - // TODO: use .domains file to list all domains, to keep users from getting rate-limited - _, _ = targetRepo, targetBranch - /*canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) - if sni != canonicalDomain { - return FallbackCertificate(), nil - }*/ } - // limit users to 1 certificate per week - - var cert, key []byte if tlsCertificate, ok := keyCache.Get(sni); ok { // we can use an existing certificate object return tlsCertificate.(*tls.Certificate), nil - } else if ok, err := keyDatabase.Has(sniBytes); err != nil { + } + + var tlsCertificate tls.Certificate + if ok, err := keyDatabase.Has(sniBytes); err != nil { // key database is not working panic(err) } else if ok { // parse certificate from database - - cert, err = keyDatabase.Get(sniBytes) + certPem, err := keyDatabase.Get(sniBytes) if err != nil { // key database is not working panic(err) } - key, err = keyDatabase.Get(append(sniBytes, '/', 'k', 'e', 'y')) + keyPem, err := keyDatabase.Get(append(sniBytes, '/', 'k', 'e', 'y')) if err != nil { // key database is not working or key doesn't exist panic(err) } - } else { - // request a new certificate + tlsCertificate, err = tls.X509KeyPair(certPem, keyPem) + if err != nil { + panic(err) + } + tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) + if err != nil { + panic(err) + } + } + if tlsCertificate.Certificate == nil || !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-24 * time.Hour)) { + // request a new certificate if bytes.Equal(sniBytes, MainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - log.Printf("Requesting new certificate for %s", sni) - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + err := CheckUserLimit(targetOwner) if err != nil { return nil, err } - key = x509.MarshalPKCS1PrivateKey(privateKey) - acmeClient, err := acmeClientFromPool(targetOwner) - if err != nil { - // TODO - } - res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ - Domains: []string{sni}, - PrivateKey: key, - Bundle: true, - MustStaple: true, - }) + + tlsCertificate, err = obtainCert(acmeClient, []string{sni}) if err != nil { return nil, err } - log.Printf("Obtained certificate for %s", sni) - err = keyDatabase.Put(append(sniBytes, '/', 'k', 'e', 'y'), key) - if err != nil { - return nil, err - } - err = keyDatabase.Put(sniBytes, res.Certificate) - if err != nil { - _ = keyDatabase.Delete(append(sniBytes, '/', 'k', 'e', 'y')) - return nil, err - } - cert = res.Certificate - } - tlsCertificate, err := tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ - Bytes: cert, - Type: "CERTIFICATE", - }), pem.EncodeToMemory(&pem.Block{ - Bytes: key, - Type: "RSA PRIVATE KEY", - })) - if err != nil { - panic(err) } - err = keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) + err := keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) if err != nil { panic(err) } @@ -178,76 +149,28 @@ var tlsConfig = &tls.Config{ }, } -// GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty -// string for custom domains. -func GetHSTSHeader(host []byte) string { - if bytes.HasSuffix(host, MainDomainSuffix) || bytes.Equal(host, RawDomain) { - return "max-age=63072000; includeSubdomains; preload" - } else { - return "" - } -} - var challengeCache = mcache.New() var keyCache = mcache.New() var keyDatabase *pogreb.DB -var fallbackCertificate *tls.Certificate -// FallbackCertificate generates a new self-signed TLS certificate on demand. -func FallbackCertificate() *tls.Certificate { - if fallbackCertificate != nil { - return fallbackCertificate +func CheckUserLimit(user string) (error) { + userLimit, ok := acmeClientCertificateLimitPerUser[user] + if !ok { + // Each Codeberg user can only add 10 new domains per day. + userLimit = equalizer.NewTokenBucket(10, time.Hour * 24) + acmeClientCertificateLimitPerUser[user] = userLimit } - - fallbackSerial, err := rand.Int(rand.Reader, (&big.Int{}).Lsh(big.NewInt(1), 159)) - if err != nil { - panic(err) + if !userLimit.Ask() { + return errors.New("rate limit exceeded: 10 certificates per user per 24 hours") } - - fallbackCertKey, err := rsa.GenerateKey(rand.Reader, 1024) - if err != nil { - panic(err) - } - - fallbackCertSpecification := &x509.Certificate{ - Subject: pkix.Name{ - CommonName: strings.TrimPrefix(string(MainDomainSuffix), "."), - }, - SerialNumber: fallbackSerial, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(100, 0, 0), - } - - fallbackCertBytes, err := x509.CreateCertificate( - rand.Reader, - fallbackCertSpecification, - fallbackCertSpecification, - fallbackCertKey.Public(), - fallbackCertKey, - ) - if err != nil { - panic(err) - } - - fallbackCert, err := tls.X509KeyPair(pem.EncodeToMemory(&pem.Block{ - Bytes: fallbackCertBytes, - Type: "CERTIFICATE", - }), pem.EncodeToMemory(&pem.Block{ - Bytes: x509.MarshalPKCS1PrivateKey(fallbackCertKey), - Type: "RSA PRIVATE KEY", - })) - if err != nil { - panic(err) - } - - fallbackCertificate = &fallbackCert - return fallbackCertificate + return nil } type AcmeAccount struct { Email string Registration *registration.Resource key crypto.PrivateKey + limit equalizer.Limiter } func (u *AcmeAccount) GetEmail() string { return u.Email @@ -259,17 +182,54 @@ func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { return u.key } -// rate-limit certificates per owner, based on LE Rate Limits: -// - 300 new orders per account per 3 hours -// - 20 requests per second -// - 10 Accounts per IP per 3 hours -var acmeClientPool []*lego.Client -var lastAcmeClient = 0 -var acmeClientRequestLimit = equalizer.NewTokenBucket(10, time.Second) // LE allows 20 requests per second, but we want to give other applications a chancem so we want 10 here at most. -var acmeClientRegistrationLimit = equalizer.NewTokenBucket(5, time.Hour * 3) // LE allows 10 registrations in 3 hours per IP, we want at most 5 of them. -var acmeClientCertificateLimitPerRegistration = []*equalizer.TokenBucket{} +func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego.Client { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + myUser := AcmeAccount{ + Email: envOr("ACME_EMAIL", "noreply@example.email"), + key: privateKey, + } + config := lego.NewConfig(&myUser) + config.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") + config.Certificate.KeyType = certcrypto.RSA2048 + acmeClient, err := lego.NewClient(config) + if err != nil { + panic(err) + } + err = configureChallenge(acmeClient.Challenge) + if err != nil { + panic(err) + } + + // accept terms + reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if err != nil { + panic(err) + } + myUser.Registration = reg + + return acmeClient +} + +var acmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { + return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) +}) var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} +var mainDomainAcmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { + if os.Getenv("DNS_PROVIDER") == "" { + // using mock server, don't use wildcard certs + return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + } + provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) + if err != nil { + return err + } + return challenge.SetDNS01Provider(provider) +}) + type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { @@ -280,67 +240,42 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { return nil } -func acmeClientFromPool(user string) (*lego.Client, error) { - userLimit, ok := acmeClientCertificateLimitPerUser[user] - if !ok { - // Each Codeberg user can only add 10 new domains per day. - userLimit = equalizer.NewTokenBucket(10, time.Hour * 24) - acmeClientCertificateLimitPerUser[user] = userLimit - - } - if !userLimit.Ask() { - return nil, errors.New("rate limit exceeded: 10 certificates per user per 24 hours") +func obtainCert(acmeClient *lego.Client, domains []string) (tls.Certificate, error) { + name := domains[0] + if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { + domains = domains[1:] } - if len(acmeClientPool) < 1 { - acmeClientPool = append(acmeClientPool, newAcmeClient()) - acmeClientCertificateLimitPerRegistration = append(acmeClientCertificateLimitPerRegistration, equalizer.NewTokenBucket(290, time.Hour * 3)) + log.Printf("Requesting new certificate for %v", domains) + res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ + Domains: domains, + Bundle: true, + MustStaple: true, + }) + if err != nil { + log.Printf("Couldn't obtain certificate for %v: %s", domains, err) + return tls.Certificate{}, err } - if !acmeClientCertificateLimitPerRegistration[(lastAcmeClient + 1) % len(acmeClientPool)].Ask() { + log.Printf("Obtained certificate for %v", domains) - } - equalizer.NewTokenBucket(290, time.Hour * 3) // LE allows 300 certificates per account, to be sure to catch it earlier, we limit that to 290. - - // TODO: limit domains by file in repo -} - -func newAcmeClient() *lego.Client { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + err = keyDatabase.Put([]byte(name + "/key"), res.PrivateKey) if err != nil { panic(err) } - myUser := AcmeAccount{ - Email: "", - key: privateKey, - } - config := lego.NewConfig(&myUser) - config.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") - config.Certificate.KeyType = certcrypto.RSA2048 - acmeClient, err := lego.NewClient(config) - if err != nil { - panic(err) - } - err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + err = keyDatabase.Put([]byte(name), res.Certificate) if err != nil { + _ = keyDatabase.Delete([]byte(name + "/key")) panic(err) } - // accept terms - if os.Getenv("ACME_ACCEPT_TERMS") == "true" { - reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) - if err != nil { - panic(err) - } - myUser.Registration = reg - } else { - log.Printf("Warning: not using ACME certificates as ACME_ACCEPT_TERMS is false!") + tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) + if err != nil { + panic(err) } - return acmeClient + return tlsCertificate, nil } func init() { - FallbackCertificate() - var err error keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, @@ -351,50 +286,62 @@ func init() { panic(err) } - // generate certificate for main domain - if os.Getenv("ACME_ACCEPT_TERMS") != "true" || os.Getenv("DNS_PROVIDER") == "" { - err = keyCache.Set(string(MainDomainSuffix), FallbackCertificate(), mcache.TTL_FOREVER) - if err != nil { - panic(err) - } - } else { - log.Printf("Requesting new certificate for *%s", MainDomainSuffix) - dnsAcmeClient, err := lego.NewClient(config) - if err != nil { - panic(err) - } - provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) - if err != nil { - panic(err) - } - err = dnsAcmeClient.Challenge.SetDNS01Provider(provider) - if err != nil { - panic(err) - } - mainPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - panic(err) - } - mainKey := x509.MarshalPKCS1PrivateKey(mainPrivateKey) - res, err := dnsAcmeClient.Certificate.Obtain(certificate.ObtainRequest{ - Domains: []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, - PrivateKey: mainKey, - Bundle: true, - MustStaple: true, - }) - if err != nil { - panic(err) - } - err = keyDatabase.Put(append(MainDomainSuffix, '/', 'k', 'e', 'y'), mainKey) - if err != nil { - panic(err) - } - err = keyDatabase.Put(MainDomainSuffix, res.Certificate) - if err != nil { - _ = keyDatabase.Delete(append(MainDomainSuffix, '/', 'k', 'e', 'y')) - panic(err) - } + if os.Getenv("ACME_ACCEPT_TERMS") != "true" || (os.Getenv("DNS_PROVIDER") == "" && os.Getenv("ACME_API") != "https://acme.mock.directory") { + panic(errors.New("you must set ACME_ACCEPT_TERMS and DNS_PROVIDER, unless ACME_API is set to https://acme.mock.directory")) } -} -// TODO: renew & revoke + go (func() { + for { + err := keyDatabase.Sync() + if err != nil { + log.Printf("Syncinc key database failed: %s", err) + } + time.Sleep(5 * time.Minute) + } + })() + go (func() { + for { + // clean up expired certs + keySuffix := []byte("/key") + now := time.Now() + expiredCertCount := 0 + key, value, err := keyDatabase.Items().Next() + for err == nil { + if !bytes.HasSuffix(key, keySuffix) { + tlsCertificates, err := certcrypto.ParsePEMBundle(value) + if err != nil || !tlsCertificates[0].NotAfter.After(now) { + err := keyDatabase.Delete(key) + if err != nil { + log.Printf("Deleting expired certificate for %s failed: %s", string(key), err) + } else { + expiredCertCount++ + } + } + } + key, value, err = keyDatabase.Items().Next() + } + log.Printf("Removed %d expired certificates from the database", expiredCertCount) + + // compact the database + result, err := keyDatabase.Compact() + if err != nil { + log.Printf("Compacting key database failed: %s", err) + } else { + log.Printf("Compacted key database (%+v)", result) + } + + // update main cert + certPem, err := keyDatabase.Get(MainDomainSuffix) + if err != nil { + // key database is not working + panic(err) + } + tlsCertificates, err := certcrypto.ParsePEMBundle(certPem) + if err != nil || !tlsCertificates[0].NotAfter.After(time.Now().Add(-48 * time.Hour)) { + _, _ = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}) + } + + time.Sleep(12 * time.Hour) + } + })() +} diff --git a/domains.go b/domains.go index 5b66551..0731672 100644 --- a/domains.go +++ b/domains.go @@ -68,10 +68,16 @@ var CanonicalDomainCacheTimeout = 15*time.Minute var canonicalDomainCache = mcache.New() // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonicalDomain string) { - // Check if the canonical domain matches +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain string) (canonicalDomain string, valid bool) { + domains := []string{} if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { - canonicalDomain = cachedValue.(string) + domains = cachedValue.([]string) + for _, domain := range domains { + if domain == actualDomain { + valid = true + break + } + } } else { req := fasthttp.AcquireRequest() req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains") @@ -79,18 +85,28 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch string) (canonic err := upstreamClient.Do(req, res) if err == nil && res.StatusCode() == fasthttp.StatusOK { - canonicalDomain = strings.TrimSpace(string(res.Body())) - if strings.Contains(canonicalDomain, "/") { - canonicalDomain = "" + for _, domain := range strings.Split(string(res.Body()), "\n") { + domain = strings.ToLower(domain) + domain = strings.TrimSpace(domain) + domain = strings.TrimPrefix(domain, "http://") + domain = strings.TrimPrefix(domain, "https://") + if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { + domains = append(domains, domain) + } + if domain == actualDomain { + valid = true + } } } - if canonicalDomain == "" { - canonicalDomain = targetOwner + string(MainDomainSuffix) - if targetRepo != "" && targetRepo != "pages" { - canonicalDomain += "/" + targetRepo - } + domains = append(domains, targetOwner + string(MainDomainSuffix)) + if domains[len(domains) - 1] == actualDomain { + valid = true } - _ = canonicalDomainCache.Set(targetOwner + "/" + targetRepo + "/" + targetBranch, canonicalDomain, CanonicalDomainCacheTimeout) + if targetRepo != "" && targetRepo != "pages" { + domains[len(domains) - 1] += "/" + targetRepo + } + _ = canonicalDomainCache.Set(targetOwner + "/" + targetRepo + "/" + targetBranch, domains, CanonicalDomainCacheTimeout) } + canonicalDomain = domains[0] return } diff --git a/handler.go b/handler.go index ee52257..53bdba6 100644 --- a/handler.go +++ b/handler.go @@ -28,8 +28,10 @@ func handler(ctx *fasthttp.RequestCtx) { // Enable caching, but require revalidation to reduce confusion ctx.Response.Header.Set("Cache-Control", "must-revalidate") + trimmedHost := TrimHostPort(ctx.Request.Host()) + // Add HSTS for RawDomain and MainDomainSuffix - if hsts := GetHSTSHeader(ctx.Host()); hsts != "" { + if hsts := GetHSTSHeader(trimmedHost); hsts != "" { ctx.Response.Header.Set("Strict-Transport-Security", hsts) } @@ -52,7 +54,7 @@ func handler(ctx *fasthttp.RequestCtx) { if ctx.IsOptions() { allowCors := false for _, allowedCorsDomain := range AllowedCorsDomains { - if bytes.Equal(ctx.Request.Host(), allowedCorsDomain) { + if bytes.Equal(trimmedHost, allowedCorsDomain) { allowCors = true break } @@ -109,8 +111,8 @@ func handler(ctx *fasthttp.RequestCtx) { // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. var tryUpstream = func() { // check if a canonical domain exists on a request on MainDomain - if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { - canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) + if bytes.HasSuffix(trimmedHost, MainDomainSuffix) { + canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "") if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(MainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -129,7 +131,7 @@ func handler(ctx *fasthttp.RequestCtx) { s.Step("preparations") - if RawDomain != nil && bytes.Equal(ctx.Request.Host(), RawDomain) { + if RawDomain != nil && bytes.Equal(trimmedHost, RawDomain) { // Serve raw content from RawDomain s.Debug("raw domain") @@ -169,12 +171,12 @@ func handler(ctx *fasthttp.RequestCtx) { return } - } else if bytes.HasSuffix(ctx.Request.Host(), MainDomainSuffix) { + } else if bytes.HasSuffix(trimmedHost, MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix s.Debug("main domain suffix") pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") - targetOwner = string(bytes.TrimSuffix(ctx.Request.Host(), MainDomainSuffix)) + targetOwner = string(bytes.TrimSuffix(trimmedHost, MainDomainSuffix)) targetRepo = pathElements[0] targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") @@ -235,8 +237,10 @@ func handler(ctx *fasthttp.RequestCtx) { returnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { + trimmedHostStr := string(trimmedHost) + // Serve pages from external domains - targetOwner, targetRepo, targetBranch = getTargetFromDNS(string(ctx.Request.Host())) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr) if targetOwner == "" { ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) return @@ -253,8 +257,11 @@ func handler(ctx *fasthttp.RequestCtx) { // Try to use the given repo on the given branch or the default branch s.Step("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain := checkCanonicalDomain(targetOwner, targetRepo, targetBranch) - if canonicalDomain != string(ctx.Request.Host()) { + canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr) + if !valid { + returnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) + return + } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0]) if targetOwner != "" { @@ -282,6 +289,9 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetStatusCode(code) ctx.Response.Header.SetContentType("text/html; charset=utf-8") message := fasthttp.StatusMessage(code) + if code == fasthttp.StatusMisdirectedRequest { + message += " - domain not specified in .domains file" + } if code == fasthttp.StatusFailedDependency { message += " - owner, repo or branch doesn't exist" } diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..506573b --- /dev/null +++ b/helpers.go @@ -0,0 +1,21 @@ +package main + +import "bytes" + +// GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty +// string for custom domains. +func GetHSTSHeader(host []byte) string { + if bytes.HasSuffix(host, MainDomainSuffix) || bytes.Equal(host, RawDomain) { + return "max-age=63072000; includeSubdomains; preload" + } else { + return "" + } +} + +func TrimHostPort(host []byte) []byte { + i := bytes.IndexByte(host, ':') + if i >= 0 { + return host[:i] + } + return host +} diff --git a/main.go b/main.go index 9fda1bc..61ff168 100644 --- a/main.go +++ b/main.go @@ -94,7 +94,6 @@ func main() { Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! MaxConnsPerIP: 100, } - //fasthttp2.ConfigureServerAndConfig(server, tlsConfig) // Setup listener and TLS listener, err := net.Listen("tcp", address) From d1ed16c0f289db6f9b9b3f92e14624e6ceed5c81 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 15:31:10 +0100 Subject: [PATCH 033/320] Update everything & add Justfile --- Justfile | 9 ++ go.mod | 6 +- go.sum | 284 ++++++++++++++++++++++++++++++------------------------- 3 files changed, 166 insertions(+), 133 deletions(-) create mode 100644 Justfile diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..1fbbba3 --- /dev/null +++ b/Justfile @@ -0,0 +1,9 @@ +dev: + #!/usr/bin/env bash + set -euxo pipefail + export ACME_API=https://acme.mock.directory + export ACME_ACCEPT_TERMS=true + export PAGES_DOMAIN=localhost.mock.directory + export RAW_DOMAIN=raw.localhost.mock.directory + export PORT=4430 + go run . diff --git a/go.mod b/go.mod index 5920606..fd52ef1 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,8 @@ go 1.16 require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/akrylysov/pogreb v0.10.1 - github.com/andybalholm/brotli v1.0.3 // indirect - github.com/go-acme/lego/v4 v4.4.0 - github.com/klauspost/compress v1.13.1 // indirect + github.com/go-acme/lego/v4 v4.5.3 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad - github.com/valyala/fasthttp v1.28.0 + github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index 0180b2f..c93f985 100644 --- a/go.sum +++ b/go.sum @@ -22,34 +22,35 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= -github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/azure/auth v0.1.0 h1:YgO/vSnJEc76NLw2ecIXvXa8bDWiqf1pOJzARAoZsYU= -github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= -github.com/Azure/go-autorest/autorest/azure/cli v0.1.0 h1:YTtBrcb6mhA+PoSW8WxFDoIIyjp13XqJeX80ssQtri4= -github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/to v0.2.0 h1:nQOZzFCudTh+TvquAtCRjM01VEYx85e9qbwt5ncW4L8= -github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= -github.com/Azure/go-autorest/autorest/validation v0.1.0 h1:ISSNzGUh+ZSzizJWOWzs8bwpXIePbGLW4z/AmUFGH5A= -github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.19 h1:7/IqD2fEYVha1EPeaiytVKhzmPV223pfkRIQUGOK2IE= +github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= +github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -57,25 +58,21 @@ github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0 h1:NNsy0ugDFZbu6S11NlsR4Kmko9hJ00gv9U2JHbwpf3g= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.976 h1:I9fs4eZbZqimF3TstEqEwK66R2b7QKd6D6OCxibSD60= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.976/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbxuNdz3FeyvHFnRjmo= +github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= -github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.37.27 h1:zh+56ktUEUjA5lMsolBnjVDaYOWadwCwx1DMF7tFM8Y= -github.com/aws/aws-sdk-go v1.37.27/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= +github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -84,17 +81,16 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= -github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc= -github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.14.0 h1:gFqGlGl/5f9UGXAaKapCGUfaTCgRKKnzu2VvzMZlOFA= -github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/cloudflare/cloudflare-go v0.20.0 h1:y2a6KwYHTFxhw+8PLhz0q5hpTGj6Un3W1pbpQLhzFaE= +github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -108,33 +104,33 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.3.11 h1:Nd3tDQfqgquLmCzyRONHzs5SJEwPPoQcFZxT8MKt1Hs= -github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpAMecnw= +github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/dnsimple/dnsimple-go v0.63.0 h1:0doY8VW/ckRIMTmOw4E1vwqo+bhtjDzvh1pU2ZteFGA= -github.com/dnsimple/dnsimple-go v0.63.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/dnsimple/dnsimple-go v0.70.1 h1:cSZndVjttLpgplDuesY4LFIvfKf/zRA1J7mCATBbzSM= +github.com/dnsimple/dnsimple-go v0.70.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/exoscale/egoscale v0.46.0 h1:i1Ut7oqFaOV8opNP9CHcwTnHryIAcTvL3pwZusQEGrA= -github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc= +github.com/exoscale/egoscale v0.67.0 h1:qgWh7T5IZGrNWtg6ib4dr+76WThvB+odTtGG+DGbXF8= +github.com/exoscale/egoscale v0.67.0/go.mod h1:wi0myUxPsV8SdEtdJHQJxFLL/wEw9fiw9Gs1PWRkvkM= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc= -github.com/go-acme/lego/v4 v4.4.0/go.mod h1:l3+tFUFZb590dWcqhWZegynUthtaHJbG2fevUpoOOE0= -github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-acme/lego/v4 v4.5.3 h1:v5RSN8l+RAeNHKTSL80eqLiec6q6UNaFpl2Df5x/5tM= +github.com/go-acme/lego/v4 v4.5.3/go.mod h1:mL1DY809LzjvRuaxINNxsI26f5oStVhBGTpJMiinkZM= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -144,15 +140,17 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= @@ -171,9 +169,16 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -181,11 +186,14 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -206,13 +214,11 @@ github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15 github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= @@ -226,8 +232,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= -github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -242,7 +248,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= @@ -250,6 +255,7 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= +github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g= github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -271,9 +277,8 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcM github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -290,10 +295,10 @@ github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPC github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= -github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/linode/linodego v0.25.3 h1:OKUepYBitakHsnI75maYefrkBtOz6CGt/4o4CKnMlnM= -github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE= +github.com/linode/linodego v0.31.1 h1:dBtjKo7J9UhNFhTOclEXb12RRyQDaRBxISdONVuU+DA= +github.com/linode/linodego v0.31.1/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.5 h1:CT4cdXzJXmo0bon298kS7NeSk+Gt8/UHpWBBol1NGCA= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= @@ -303,6 +308,8 @@ github.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOE github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -322,8 +329,8 @@ github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4f github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= -github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -334,8 +341,9 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -348,24 +356,31 @@ github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y= github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= -github.com/nrdcg/desec v0.5.0 h1:foL7hqivYOMlv0qDhHXJtuuEXkqf0wW9EQMqyrt228g= -github.com/nrdcg/desec v0.5.0/go.mod h1:2ejvMazkav1VdDbv2HeQO7w+Ta1CGHqzQr27ZBYTuEQ= +github.com/nrdcg/desec v0.6.0 h1:kZ9JtsYEW3LNfuPIM+2tXoxoQlF9koWfQTWTQsA7Sr8= +github.com/nrdcg/desec v0.6.0/go.mod h1:wybWg5cRrNmtXLYpUCPCLvz4jfFNEGZQEnoUiX9WqcY= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/freemyip v0.2.0 h1:/GscavT4GVqAY13HExl5UyoB4wlchv6Cg5NYDGsUoJ8= +github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4= github.com/nrdcg/goinwx v0.8.1 h1:20EQ/JaGFnSKwiDH2JzjIpicffl3cPk6imJBDqVBVtU= github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c= github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/nrdcg/porkbun v0.1.1 h1:gxVzQYfFUGXhnBax/aVugoE3OIBAdHgrJgyMPyY5Sjo= github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= +github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= @@ -375,7 +390,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -388,27 +402,22 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -435,6 +444,10 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= +github.com/softlayer/softlayer-go v1.0.3 h1:9FONm5xzQ9belQtbdryR6gBg4EF6hX6lrjNKi0IvZkU= +github.com/softlayer/softlayer-go v1.0.3/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4= +github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= +github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -457,37 +470,36 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE= -github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= +github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggsC4564= +github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.28.0 h1:ruVmTmZaBR5i67NqnjvvH5gEv0zwHfWtbjoyW98iho4= -github.com/valyala/fasthttp v1.28.0/go.mod h1:cmWIqlu99AO/RKcp1HWaViTqc57FswJOfYYdPJBl8BA= +github.com/valyala/fasthttp v1.31.0 h1:lrauRLII19afgCs2fnWRJ4M5IkV0lo2FqA61uGkNBfE= +github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= -github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ= -github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs= +github.com/vultr/govultr/v2 v2.7.1 h1:uF9ERet++Gb+7Cqs3p1P6b6yebeaZqVd7t5P2uZCaJU= +github.com/vultr/govultr/v2 v2.7.1/go.mod h1:BvOhVe6/ZpjwcoL6/unkdQshmbS9VGbowI4QT+3DGVU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -503,18 +515,18 @@ golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -544,6 +556,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -552,7 +565,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -563,19 +575,22 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -587,8 +602,10 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -597,11 +614,9 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -611,12 +626,11 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -627,14 +641,20 @@ golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -643,17 +663,17 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= +golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -676,7 +696,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -687,11 +706,13 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -728,9 +749,7 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -738,13 +757,21 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= @@ -754,11 +781,11 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ns1/ns1-go.v2 v2.4.4 h1:xV/rJE2m8p3IncnwMEFrv36K4NDnRXu4CoIkgq+AZek= -gopkg.in/ns1/ns1-go.v2 v2.4.4/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= +gopkg.in/ns1/ns1-go.v2 v2.6.2 h1:tC+gRSN6fmnb9l9cVrIysXyuRO0wV6cZrjDqlMB0gGc= +gopkg.in/ns1/ns1-go.v2 v2.6.2/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -773,7 +800,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From fcccd6435a503f30039a2217fe156ec24690e254 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 15:37:36 +0100 Subject: [PATCH 034/320] Add EAB for ACME --- README.md | 1 + certificates.go | 20 ++++++++++++++++---- handler.go | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 70d4ebe..3849f8f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. - `ACME_API` (default: https://acme.zerossl.com/v2/DV90): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL is used as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt). - `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. +- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. diff --git a/certificates.go b/certificates.go index 3ba6f9e..80edc34 100644 --- a/certificates.go +++ b/certificates.go @@ -204,11 +204,23 @@ func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego } // accept terms - reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) - if err != nil { - panic(err) + if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { + reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if err != nil { + panic(err) + } + myUser.Registration = reg + } else { + reg, err := acmeClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", + Kid: os.Getenv("ACME_EAB_KID"), + HmacEncoded: os.Getenv("ACME_EAB_HMAC"), + }) + if err != nil { + panic(err) + } + myUser.Registration = reg } - myUser.Registration = reg return acmeClient } diff --git a/handler.go b/handler.go index 53bdba6..662809a 100644 --- a/handler.go +++ b/handler.go @@ -148,7 +148,7 @@ func handler(ctx *fasthttp.RequestCtx) { targetOwner = pathElements[0] targetRepo = pathElements[1] - // raw.codeberg.page/example/myrepo/@main/index.html + // raw.codeberg.org/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { s.Step("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], From 77321eb1816d6ee323539883f4751fbe6546dce7 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 15:54:52 +0100 Subject: [PATCH 035/320] Make ACME account persistent & fix issues with certificate resolution --- certificates.go | 223 +++++++++++++++++++++++++++++++----------------- domains.go | 4 +- main.go | 2 + 3 files changed, 150 insertions(+), 79 deletions(-) diff --git a/certificates.go b/certificates.go index 80edc34..d066ed4 100644 --- a/certificates.go +++ b/certificates.go @@ -8,6 +8,7 @@ import ( "crypto/rand" "crypto/tls" "crypto/x509" + "encoding/json" "errors" "github.com/OrlovEvgeny/go-mcache" "github.com/akrylysov/pogreb/fs" @@ -16,9 +17,11 @@ import ( "github.com/go-acme/lego/v4/challenge/resolver" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/providers/dns" + "io/ioutil" "log" "os" "strings" + "sync" "time" "github.com/akrylysov/pogreb" @@ -83,38 +86,30 @@ var tlsConfig = &tls.Config{ } var tlsCertificate tls.Certificate - if ok, err := keyDatabase.Has(sniBytes); err != nil { - // key database is not working - panic(err) - } else if ok { - // parse certificate from database - certPem, err := keyDatabase.Get(sniBytes) - if err != nil { - // key database is not working - panic(err) - } - keyPem, err := keyDatabase.Get(append(sniBytes, '/', 'k', 'e', 'y')) - if err != nil { - // key database is not working or key doesn't exist - panic(err) - } - - tlsCertificate, err = tls.X509KeyPair(certPem, keyPem) - if err != nil { - panic(err) - } + var err error + var ok bool + if tlsCertificate, ok = retrieveCertFromDB(sniBytes); ok { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { panic(err) } + + if !bytes.Equal(sniBytes, MainDomainSuffix) && !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { + go (func() { + tlsCertificate, err = obtainCert(acmeClient, []string{sni}) + if err != nil { + log.Printf("Couldn't renew certificate.") + } + })() + } } - if tlsCertificate.Certificate == nil || !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-24 * time.Hour)) { + if tlsCertificate.Certificate == nil || !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-5 * time.Minute)) { // request a new certificate if bytes.Equal(sniBytes, MainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - err := CheckUserLimit(targetOwner) + err = CheckUserLimit(targetOwner) if err != nil { return nil, err } @@ -125,7 +120,7 @@ var tlsConfig = &tls.Config{ } } - err := keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) + err = keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) if err != nil { panic(err) } @@ -133,6 +128,7 @@ var tlsConfig = &tls.Config{ }, PreferServerCipherSuites: true, NextProtos: []string{ + "http/1.1", tlsalpn01.ACMETLS1Protocol, }, @@ -166,11 +162,14 @@ func CheckUserLimit(user string) (error) { return nil } +var myAcmeAccount AcmeAccount +var myAcmeConfig *lego.Config + type AcmeAccount struct { Email string Registration *registration.Resource - key crypto.PrivateKey - limit equalizer.Limiter + Key crypto.PrivateKey `json:"-"` + KeyPEM string `json:"Key"` } func (u *AcmeAccount) GetEmail() string { return u.Email @@ -179,22 +178,11 @@ func (u AcmeAccount) GetRegistration() *registration.Resource { return u.Registration } func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { - return u.key + return u.Key } func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego.Client { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - panic(err) - } - myUser := AcmeAccount{ - Email: envOr("ACME_EMAIL", "noreply@example.email"), - key: privateKey, - } - config := lego.NewConfig(&myUser) - config.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") - config.Certificate.KeyType = certcrypto.RSA2048 - acmeClient, err := lego.NewClient(config) + acmeClient, err := lego.NewClient(myAcmeConfig) if err != nil { panic(err) } @@ -202,46 +190,12 @@ func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego if err != nil { panic(err) } - - // accept terms - if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { - reg, err := acmeClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) - if err != nil { - panic(err) - } - myUser.Registration = reg - } else { - reg, err := acmeClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", - Kid: os.Getenv("ACME_EAB_KID"), - HmacEncoded: os.Getenv("ACME_EAB_HMAC"), - }) - if err != nil { - panic(err) - } - myUser.Registration = reg - } - return acmeClient } -var acmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { - return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) -}) +var acmeClient, mainDomainAcmeClient *lego.Client var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} -var mainDomainAcmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { - if os.Getenv("DNS_PROVIDER") == "" { - // using mock server, don't use wildcard certs - return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) - } - provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) - if err != nil { - return err - } - return challenge.SetDNS01Provider(provider) -}) - type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { @@ -252,13 +206,52 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { return nil } +func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { + // parse certificate from database + certPem, err := keyDatabase.Get(sni) + if err != nil { + // key database is not working + panic(err) + } + if certPem == nil { + return tls.Certificate{}, false + } + keyPem, err := keyDatabase.Get(append(sni, '/', 'k', 'e', 'y')) + if err != nil { + // key database is not working or key doesn't exist + panic(err) + } + + tlsCertificate, err := tls.X509KeyPair(certPem, keyPem) + if err != nil { + panic(err) + } + return tlsCertificate, true +} + +var obtainLocks = sync.Map{} func obtainCert(acmeClient *lego.Client, domains []string) (tls.Certificate, error) { - name := domains[0] + name := strings.TrimPrefix(domains[0], "*") if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] } - log.Printf("Requesting new certificate for %v", domains) + // lock to avoid simultaneous requests + _, working := obtainLocks.LoadOrStore(name, struct{}{}) + if working { + for working { + time.Sleep(100 * time.Millisecond) + _, working = obtainLocks.Load(name) + } + cert, ok := retrieveCertFromDB([]byte(name)) + if !ok { + return tls.Certificate{}, errors.New("certificate failed in synchronous request") + } + return cert, nil + } + defer obtainLocks.Delete(name) + + log.Printf("Requesting new certificate for %v", domains) res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: domains, Bundle: true, @@ -272,22 +265,24 @@ func obtainCert(acmeClient *lego.Client, domains []string) (tls.Certificate, err err = keyDatabase.Put([]byte(name + "/key"), res.PrivateKey) if err != nil { + obtainLocks.Delete(name) panic(err) } err = keyDatabase.Put([]byte(name), res.Certificate) if err != nil { _ = keyDatabase.Delete([]byte(name + "/key")) + obtainLocks.Delete(name) panic(err) } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { - panic(err) + return tls.Certificate{}, err } return tlsCertificate, nil } -func init() { +func setupCertificates() { var err error keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, @@ -302,6 +297,80 @@ func init() { panic(errors.New("you must set ACME_ACCEPT_TERMS and DNS_PROVIDER, unless ACME_API is set to https://acme.mock.directory")) } + if account, err := ioutil.ReadFile("acme-account.json"); err == nil { + err = json.Unmarshal(account, &myAcmeAccount) + if err != nil { + panic(err) + } + myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM)) + if err != nil { + panic(err) + } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) + myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") + myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) + } else if os.IsNotExist(err) { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + myAcmeAccount = AcmeAccount{ + Email: envOr("ACME_EMAIL", "noreply@example.email"), + Key: privateKey, + KeyPEM: string(certcrypto.PEMEncode(privateKey)), + } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) + myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") + myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + tempClient := newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) + + // accept terms & log in to EAB + if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if err != nil { + panic(err) + } + myAcmeAccount.Registration = reg + } else { + reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", + Kid: os.Getenv("ACME_EAB_KID"), + HmacEncoded: os.Getenv("ACME_EAB_HMAC"), + }) + if err != nil { + panic(err) + } + myAcmeAccount.Registration = reg + } + + acmeAccountJson, err := json.Marshal(myAcmeAccount) + if err != nil { + panic(err) + } + err = ioutil.WriteFile("acme-account.json", acmeAccountJson, 0600) + if err != nil { + panic(err) + } + } else { + panic(err) + } + + acmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { + return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + }) + mainDomainAcmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { + if os.Getenv("DNS_PROVIDER") == "" { + // using mock server, don't use wildcard certs + return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + } + provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) + if err != nil { + return err + } + return challenge.SetDNS01Provider(provider) + }) + go (func() { for { err := keyDatabase.Sync() diff --git a/domains.go b/domains.go index 0731672..0bf2605 100644 --- a/domains.go +++ b/domains.go @@ -46,10 +46,10 @@ func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch stri cnameParts := strings.Split(strings.TrimSuffix(cname, string(MainDomainSuffix)), ".") targetOwner = cnameParts[len(cnameParts)-1] if len(cnameParts) > 1 { - targetRepo = cnameParts[len(cnameParts)-1] + targetRepo = cnameParts[len(cnameParts)-2] } if len(cnameParts) > 2 { - targetBranch = cnameParts[len(cnameParts)-2] + targetBranch = cnameParts[len(cnameParts)-3] } if targetRepo == "" { targetRepo = "pages" diff --git a/main.go b/main.go index 61ff168..c795f1f 100644 --- a/main.go +++ b/main.go @@ -102,6 +102,8 @@ func main() { } listener = tls.NewListener(listener, tlsConfig) + setupCertificates() + // Start the web server err = server.Serve(listener) if err != nil { From b19a5ecc1d1da0f5c359d4cf29a1767d24640060 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 19:36:12 +0100 Subject: [PATCH 036/320] Switch back to Let's Encrypt again & implement renewal --- README.md | 3 +- certificates.go | 146 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 105 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 3849f8f..81f12c7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `REDIRECT_BROKEN_DNS` (default: https://docs.codeberg.org/pages/custom-domains/): info page for setting up DNS, shown for invalid DNS setups. - `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. -- `ACME_API` (default: https://acme.zerossl.com/v2/DV90): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL is used as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt). +- `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). + ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. diff --git a/certificates.go b/certificates.go index d066ed4..501ae37 100644 --- a/certificates.go +++ b/certificates.go @@ -8,6 +8,7 @@ import ( "crypto/rand" "crypto/tls" "crypto/x509" + "encoding/gob" "encoding/json" "errors" "github.com/OrlovEvgeny/go-mcache" @@ -88,22 +89,7 @@ var tlsConfig = &tls.Config{ var tlsCertificate tls.Certificate var err error var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes); ok { - tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) - if err != nil { - panic(err) - } - - if !bytes.Equal(sniBytes, MainDomainSuffix) && !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { - go (func() { - tlsCertificate, err = obtainCert(acmeClient, []string{sni}) - if err != nil { - log.Printf("Couldn't renew certificate.") - } - })() - } - } - if tlsCertificate.Certificate == nil || !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-5 * time.Minute)) { + if tlsCertificate, ok = retrieveCertFromDB(sniBytes); !ok { // request a new certificate if bytes.Equal(sniBytes, MainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") @@ -114,7 +100,7 @@ var tlsConfig = &tls.Config{ return nil, err } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil) if err != nil { return nil, err } @@ -196,6 +182,13 @@ func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego var acmeClient, mainDomainAcmeClient *lego.Client var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} +// rate limit is 300 / 3 hours, we want 200 / 2 hours but to refill more often, so that's 25 new domains every 15 minutes +// TODO: when this is used a lot, we probably have to think of a somewhat better solution? +var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15 * time.Minute) + +// rate limit is 20 / second, we want 10 / second +var acmeClientRequestLimit = equalizer.NewTokenBucket(10, 1 * time.Second) + type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { @@ -208,29 +201,50 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // parse certificate from database - certPem, err := keyDatabase.Get(sni) + resBytes, err := keyDatabase.Get(sni) if err != nil { // key database is not working panic(err) } - if certPem == nil { + if resBytes == nil { return tls.Certificate{}, false } - keyPem, err := keyDatabase.Get(append(sni, '/', 'k', 'e', 'y')) + + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + res := &certificate.Resource{} + err = resDec.Decode(res) if err != nil { - // key database is not working or key doesn't exist panic(err) } - tlsCertificate, err := tls.X509KeyPair(certPem, keyPem) + tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { panic(err) } + + if !bytes.Equal(sni, MainDomainSuffix) { + tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) + if err != nil { + panic(err) + } + + // renew certificates 7 days before they expire + if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { + go (func() { + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res) + if err != nil { + log.Printf("Couldn't renew certificate for %s: %s", sni, err) + } + })() + } + } + return tlsCertificate, true } var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -251,24 +265,36 @@ func obtainCert(acmeClient *lego.Client, domains []string) (tls.Certificate, err } defer obtainLocks.Delete(name) - log.Printf("Requesting new certificate for %v", domains) - res, err := acmeClient.Certificate.Obtain(certificate.ObtainRequest{ - Domains: domains, - Bundle: true, - MustStaple: true, - }) + // request actual cert + var res *certificate.Resource + var err error + if renew != nil { + acmeClientRequestLimit.Take() + log.Printf("Renewing certificate for %v", domains) + res, err = acmeClient.Certificate.Renew(*renew, true, false, "") + } else { + acmeClientOrderLimit.Take() + acmeClientRequestLimit.Take() + log.Printf("Requesting new certificate for %v", domains) + res, err = acmeClient.Certificate.Obtain(certificate.ObtainRequest{ + Domains: domains, + Bundle: true, + MustStaple: false, + }) + } if err != nil { log.Printf("Couldn't obtain certificate for %v: %s", domains, err) return tls.Certificate{}, err } log.Printf("Obtained certificate for %v", domains) - err = keyDatabase.Put([]byte(name + "/key"), res.PrivateKey) + var resGob bytes.Buffer + resEnc := gob.NewEncoder(&resGob) + err = resEnc.Encode(res) if err != nil { - obtainLocks.Delete(name) panic(err) } - err = keyDatabase.Put([]byte(name), res.Certificate) + err = keyDatabase.Put([]byte(name), resGob.Bytes()) if err != nil { _ = keyDatabase.Delete([]byte(name + "/key")) obtainLocks.Delete(name) @@ -307,7 +333,7 @@ func setupCertificates() { panic(err) } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") + myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) } else if os.IsNotExist(err) { @@ -321,7 +347,7 @@ func setupCertificates() { KeyPEM: string(certcrypto.PEMEncode(privateKey)), } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme.zerossl.com/v2/DV90") + myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 tempClient := newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) @@ -371,6 +397,17 @@ func setupCertificates() { return challenge.SetDNS01Provider(provider) }) + resBytes, err := keyDatabase.Get(MainDomainSuffix) + if err != nil { + // key database is not working + panic(err) + } else if resBytes == nil { + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, nil) + if err != nil { + log.Fatalf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) + } + } + go (func() { for { err := keyDatabase.Sync() @@ -383,13 +420,20 @@ func setupCertificates() { go (func() { for { // clean up expired certs - keySuffix := []byte("/key") now := time.Now() expiredCertCount := 0 - key, value, err := keyDatabase.Items().Next() + key, resBytes, err := keyDatabase.Items().Next() for err == nil { - if !bytes.HasSuffix(key, keySuffix) { - tlsCertificates, err := certcrypto.ParsePEMBundle(value) + if !bytes.Equal(key, MainDomainSuffix) { + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + res := &certificate.Resource{} + err = resDec.Decode(res) + if err != nil { + panic(err) + } + + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) if err != nil || !tlsCertificates[0].NotAfter.After(now) { err := keyDatabase.Delete(key) if err != nil { @@ -399,7 +443,7 @@ func setupCertificates() { } } } - key, value, err = keyDatabase.Items().Next() + key, resBytes, err = keyDatabase.Items().Next() } log.Printf("Removed %d expired certificates from the database", expiredCertCount) @@ -412,14 +456,30 @@ func setupCertificates() { } // update main cert - certPem, err := keyDatabase.Get(MainDomainSuffix) + resBytes, err = keyDatabase.Get(MainDomainSuffix) if err != nil { // key database is not working panic(err) } - tlsCertificates, err := certcrypto.ParsePEMBundle(certPem) - if err != nil || !tlsCertificates[0].NotAfter.After(time.Now().Add(-48 * time.Hour)) { - _, _ = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}) + + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + res := &certificate.Resource{} + err = resDec.Decode(res) + if err != nil { + panic(err) + } + + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) + + // renew main certificate 30 days before it expires + if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { + go (func() { + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res) + if err != nil { + log.Printf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) + } + })() } time.Sleep(12 * time.Hour) From 73c21d0195a10b9c20a8a73f60a3e4605aa2c974 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 20:34:12 +0100 Subject: [PATCH 037/320] Reduce file cache timeout as it doesn't seem to do much --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 662809a..086d249 100644 --- a/handler.go +++ b/handler.go @@ -313,7 +313,7 @@ type branchTimestamp struct { // FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending // on your available memory. -var FileCacheTimeout = 60*time.Minute +var FileCacheTimeout = 5*time.Minute // FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. var FileCacheSizeLimit = 1024 * 1024 // fileResponseCache stores responses from the Gitea server From c99dbb34ce8030f7975117c30af5f2eb7569b2b6 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 21:10:46 +0100 Subject: [PATCH 038/320] Implement HTTP-01 challenge --- README.md | 1 + certificates.go | 20 ++++++++++++++++++-- main.go | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81f12c7..85d47ad 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,6 @@ - `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. +- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. diff --git a/certificates.go b/certificates.go index 501ae37..89961b1 100644 --- a/certificates.go +++ b/certificates.go @@ -131,7 +131,6 @@ var tlsConfig = &tls.Config{ }, } -var challengeCache = mcache.New() var keyCache = mcache.New() var keyDatabase *pogreb.DB @@ -189,6 +188,7 @@ var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15 * time.Minute) // rate limit is 20 / second, we want 10 / second var acmeClientRequestLimit = equalizer.NewTokenBucket(10, 1 * time.Second) +var challengeCache = mcache.New() type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { @@ -198,6 +198,15 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { challengeCache.Remove(domain) return nil } +type AcmeHTTPChallengeProvider struct{} +var _ challenge.Provider = AcmeHTTPChallengeProvider{} +func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { + return challengeCache.Set(domain + "/" + token, keyAuth, 1*time.Hour) +} +func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { + challengeCache.Remove(domain + "/" + token) + return nil +} func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // parse certificate from database @@ -383,7 +392,14 @@ func setupCertificates() { } acmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { - return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + err = challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + if err != nil { + return err + } + if os.Getenv("ENABLE_HTTP_SERVER") == "true" { + return challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{}) + } + return err }) mainDomainAcmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { if os.Getenv("DNS_PROVIDER") == "" { diff --git a/main.go b/main.go index c795f1f..c181977 100644 --- a/main.go +++ b/main.go @@ -22,6 +22,7 @@ import ( "fmt" "log" "net" + "net/http" "os" "time" @@ -103,6 +104,26 @@ func main() { listener = tls.NewListener(listener, tlsConfig) setupCertificates() + if os.Getenv("ENABLE_HTTP_SERVER") == "true" { + go (func() { + challengePath := []byte("/.well-known/acme-challenge/") + err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { + if bytes.HasPrefix(ctx.Path(), challengePath) { + challenge, ok := challengeCache.Get(string(TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + if !ok { + ctx.SetStatusCode(http.StatusNotFound) + ctx.SetBodyString("no challenge for this token") + } + ctx.SetBodyString(challenge.(string)) + } else { + ctx.Redirect("https://" + string(ctx.Host()) + string(ctx.RequestURI()), http.StatusMovedPermanently) + } + }) + if err != nil { + log.Fatalf("Couldn't start HTTP server: %s", err) + } + })() + } // Start the web server err = server.Serve(listener) From f22cd6c4e05ba78d3d4ad0960b5799ead0232577 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 21:12:28 +0100 Subject: [PATCH 039/320] Make it possible to disable rate limits --- README.md | 1 + certificates.go | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85d47ad..f545228 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ - `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. +- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. diff --git a/certificates.go b/certificates.go index 89961b1..e5aca5c 100644 --- a/certificates.go +++ b/certificates.go @@ -278,12 +278,16 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re var res *certificate.Resource var err error if renew != nil { - acmeClientRequestLimit.Take() + if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { + acmeClientRequestLimit.Take() + } log.Printf("Renewing certificate for %v", domains) res, err = acmeClient.Certificate.Renew(*renew, true, false, "") } else { - acmeClientOrderLimit.Take() - acmeClientRequestLimit.Take() + if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { + acmeClientOrderLimit.Take() + acmeClientRequestLimit.Take() + } log.Printf("Requesting new certificate for %v", domains) res, err = acmeClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: domains, From eae4513b96f8a8fcf2dffff5ea4cc4345c845be1 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 21:39:40 +0100 Subject: [PATCH 040/320] Fix timeout for HTTP-01, bug in HTTP challenge response & only count domains towards user limit after acquiring locks --- certificates.go | 21 +++++++++++---------- main.go | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/certificates.go b/certificates.go index e5aca5c..0f75c53 100644 --- a/certificates.go +++ b/certificates.go @@ -95,12 +95,7 @@ var tlsConfig = &tls.Config{ return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - err = CheckUserLimit(targetOwner) - if err != nil { - return nil, err - } - - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner) if err != nil { return nil, err } @@ -241,7 +236,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // renew certificates 7 days before they expire if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { go (func() { - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res) + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "") if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) } @@ -253,7 +248,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { } var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -284,6 +279,12 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re log.Printf("Renewing certificate for %v", domains) res, err = acmeClient.Certificate.Renew(*renew, true, false, "") } else { + if user != "" { + if err := CheckUserLimit(user); err != nil { + return tls.Certificate{}, err + } + } + if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { acmeClientOrderLimit.Take() acmeClientRequestLimit.Take() @@ -422,7 +423,7 @@ func setupCertificates() { // key database is not working panic(err) } else if resBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, nil) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, nil, "") if err != nil { log.Fatalf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) } @@ -495,7 +496,7 @@ func setupCertificates() { // renew main certificate 30 days before it expires if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res, "") if err != nil { log.Printf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) } diff --git a/main.go b/main.go index c181977..700241a 100644 --- a/main.go +++ b/main.go @@ -91,7 +91,7 @@ func main() { MaxRequestBodySize: 0, NoDefaultServerHeader: true, NoDefaultDate: true, - ReadTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! MaxConnsPerIP: 100, } @@ -110,7 +110,7 @@ func main() { err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { if bytes.HasPrefix(ctx.Path(), challengePath) { challenge, ok := challengeCache.Get(string(TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) - if !ok { + if !ok || challenge == nil { ctx.SetStatusCode(http.StatusNotFound) ctx.SetBodyString("no challenge for this token") } From ce802734d45f7e2dd2f9f4282df4015d59b3d469 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 20 Nov 2021 21:43:12 +0100 Subject: [PATCH 041/320] Add build script & ignore files --- .gitignore | 2 ++ Justfile | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 983b8b9..cb42378 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea/ *.iml key-database.pogreb/ +acme-account.json +build/ diff --git a/Justfile b/Justfile index 1fbbba3..a5feb7a 100644 --- a/Justfile +++ b/Justfile @@ -7,3 +7,6 @@ dev: export RAW_DOMAIN=raw.localhost.mock.directory export PORT=4430 go run . + +build: + CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ From e5385be6daaca6a4cf4a1a3143b56b1d047afff4 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 24 Nov 2021 19:08:34 +0100 Subject: [PATCH 042/320] Fix iterator issue causing 100% CPU load --- certificates.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/certificates.go b/certificates.go index 0f75c53..d0735c6 100644 --- a/certificates.go +++ b/certificates.go @@ -443,7 +443,8 @@ func setupCertificates() { // clean up expired certs now := time.Now() expiredCertCount := 0 - key, resBytes, err := keyDatabase.Items().Next() + keyDatabaseIterator := keyDatabase.Items() + key, resBytes, err := keyDatabaseIterator.Next() for err == nil { if !bytes.Equal(key, MainDomainSuffix) { resGob := bytes.NewBuffer(resBytes) @@ -464,7 +465,7 @@ func setupCertificates() { } } } - key, resBytes, err = keyDatabase.Items().Next() + key, resBytes, err = keyDatabaseIterator.Next() } log.Printf("Removed %d expired certificates from the database", expiredCertCount) From 5ed8d0f1290c6779f5985e23003db2a91de9359a Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 24 Nov 2021 19:09:37 +0100 Subject: [PATCH 043/320] Add clarification on cache & reduce default branch cache to 15 minutes --- handler.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index 086d249..5be8a51 100644 --- a/handler.go +++ b/handler.go @@ -293,13 +293,13 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { message += " - domain not specified in .domains file" } if code == fasthttp.StatusFailedDependency { - message += " - owner, repo or branch doesn't exist" + message += " - owner, repo or branch doesn't exist (if everything's set up correctly, wait up to 15 minutes for cache invalidation)" } ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) } // BranchExistanceCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var DefaultBranchCacheTimeout = 1*time.Hour +var DefaultBranchCacheTimeout = 15*time.Minute // BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter // than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be // picked up faster, while still allowing the content to be cached longer if nothing changes. @@ -317,6 +317,7 @@ var FileCacheTimeout = 5*time.Minute // FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. var FileCacheSizeLimit = 1024 * 1024 // fileResponseCache stores responses from the Gitea server +// TODO: make this an MRU cache with a size limit var fileResponseCache = mcache.New() type fileResponse struct { exists bool From e800d2110e8f485cf18aa43209287751d91de2ff Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Nov 2021 16:12:28 +0100 Subject: [PATCH 044/320] gofmt -s -w *.go */*.go --- certificates.go | 30 +++++++++++++++++----------- debug-stepper/stepper.go | 10 +++++----- domains.go | 15 +++++++------- handler.go | 42 +++++++++++++++++++++++----------------- handler_test.go | 1 - main.go | 4 ++-- 6 files changed, 58 insertions(+), 44 deletions(-) diff --git a/certificates.go b/certificates.go index d0735c6..b7c152f 100644 --- a/certificates.go +++ b/certificates.go @@ -101,7 +101,7 @@ var tlsConfig = &tls.Config{ } } - err = keyCache.Set(sni, &tlsCertificate, 15 * time.Minute) + err = keyCache.Set(sni, &tlsCertificate, 15*time.Minute) if err != nil { panic(err) } @@ -129,11 +129,11 @@ var tlsConfig = &tls.Config{ var keyCache = mcache.New() var keyDatabase *pogreb.DB -func CheckUserLimit(user string) (error) { +func CheckUserLimit(user string) error { userLimit, ok := acmeClientCertificateLimitPerUser[user] if !ok { // Each Codeberg user can only add 10 new domains per day. - userLimit = equalizer.NewTokenBucket(10, time.Hour * 24) + userLimit = equalizer.NewTokenBucket(10, time.Hour*24) acmeClientCertificateLimitPerUser[user] = userLimit } if !userLimit.Ask() { @@ -149,8 +149,9 @@ type AcmeAccount struct { Email string Registration *registration.Resource Key crypto.PrivateKey `json:"-"` - KeyPEM string `json:"Key"` + KeyPEM string `json:"Key"` } + func (u *AcmeAccount) GetEmail() string { return u.Email } @@ -178,14 +179,17 @@ var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} // rate limit is 300 / 3 hours, we want 200 / 2 hours but to refill more often, so that's 25 new domains every 15 minutes // TODO: when this is used a lot, we probably have to think of a somewhat better solution? -var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15 * time.Minute) +var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) // rate limit is 20 / second, we want 10 / second -var acmeClientRequestLimit = equalizer.NewTokenBucket(10, 1 * time.Second) +var acmeClientRequestLimit = equalizer.NewTokenBucket(10, 1*time.Second) var challengeCache = mcache.New() + type AcmeTLSChallengeProvider struct{} + var _ challenge.Provider = AcmeTLSChallengeProvider{} + func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { return challengeCache.Set(domain, keyAuth, 1*time.Hour) } @@ -193,10 +197,13 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { challengeCache.Remove(domain) return nil } + type AcmeHTTPChallengeProvider struct{} + var _ challenge.Provider = AcmeHTTPChallengeProvider{} + func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { - return challengeCache.Set(domain + "/" + token, keyAuth, 1*time.Hour) + return challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) } func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { challengeCache.Remove(domain + "/" + token) @@ -248,6 +255,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { } var obtainLocks = sync.Map{} + func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { @@ -356,8 +364,8 @@ func setupCertificates() { panic(err) } myAcmeAccount = AcmeAccount{ - Email: envOr("ACME_EMAIL", "noreply@example.email"), - Key: privateKey, + Email: envOr("ACME_EMAIL", "noreply@example.email"), + Key: privateKey, KeyPEM: string(certcrypto.PEMEncode(privateKey)), } myAcmeConfig = lego.NewConfig(&myAcmeAccount) @@ -375,8 +383,8 @@ func setupCertificates() { } else { reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", - Kid: os.Getenv("ACME_EAB_KID"), - HmacEncoded: os.Getenv("ACME_EAB_HMAC"), + Kid: os.Getenv("ACME_EAB_KID"), + HmacEncoded: os.Getenv("ACME_EAB_HMAC"), }) if err != nil { panic(err) diff --git a/debug-stepper/stepper.go b/debug-stepper/stepper.go index 0fd0b6f..05506b6 100644 --- a/debug-stepper/stepper.go +++ b/debug-stepper/stepper.go @@ -14,9 +14,9 @@ var Logger = func(s string, i ...interface{}) { } type Stepper struct { - Name string - Start time.Time - LastStep time.Time + Name string + Start time.Time + LastStep time.Time Completion time.Time } @@ -27,8 +27,8 @@ func Start(name string) *Stepper { t := time.Now() Logger("%s: started at %s\n", name, t.Format(time.RFC3339)) return &Stepper{ - Name: name, - Start: t, + Name: name, + Start: t, LastStep: t, } } diff --git a/domains.go b/domains.go index 0bf2605..4dcaaee 100644 --- a/domains.go +++ b/domains.go @@ -9,7 +9,8 @@ import ( ) // DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. -var DnsLookupCacheTimeout = 15*time.Minute +var DnsLookupCacheTimeout = 15 * time.Minute + // dnsLookupCache stores DNS lookups for custom domains var dnsLookupCache = mcache.New() @@ -61,9 +62,9 @@ func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch stri return } - // CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. -var CanonicalDomainCacheTimeout = 15*time.Minute +var CanonicalDomainCacheTimeout = 15 * time.Minute + // canonicalDomainCache stores canonical domains var canonicalDomainCache = mcache.New() @@ -98,14 +99,14 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain st } } } - domains = append(domains, targetOwner + string(MainDomainSuffix)) - if domains[len(domains) - 1] == actualDomain { + domains = append(domains, targetOwner+string(MainDomainSuffix)) + if domains[len(domains)-1] == actualDomain { valid = true } if targetRepo != "" && targetRepo != "pages" { - domains[len(domains) - 1] += "/" + targetRepo + domains[len(domains)-1] += "/" + targetRepo } - _ = canonicalDomainCache.Set(targetOwner + "/" + targetRepo + "/" + targetBranch, domains, CanonicalDomainCacheTimeout) + _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout) } canonicalDomain = domains[0] return diff --git a/handler.go b/handler.go index 5be8a51..53e11cc 100644 --- a/handler.go +++ b/handler.go @@ -118,7 +118,7 @@ func handler(ctx *fasthttp.RequestCtx) { if targetRepo != "pages" { canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] } - ctx.Redirect("https://" + canonicalDomain + canonicalPath, fasthttp.StatusTemporaryRedirect) + ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) return } } @@ -185,7 +185,7 @@ func handler(ctx *fasthttp.RequestCtx) { if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { if targetRepo == "pages" { // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... - ctx.Redirect("/" + strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) + ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) return } @@ -299,30 +299,36 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { } // BranchExistanceCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var DefaultBranchCacheTimeout = 15*time.Minute +var DefaultBranchCacheTimeout = 15 * time.Minute + // BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter // than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be // picked up faster, while still allowing the content to be cached longer if nothing changes. -var BranchExistanceCacheTimeout = 5*time.Minute +var BranchExistanceCacheTimeout = 5 * time.Minute + // branchTimestampCache stores branch timestamps for faster cache checking var branchTimestampCache = mcache.New() + type branchTimestamp struct { - branch string + branch string timestamp time.Time } // FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending // on your available memory. -var FileCacheTimeout = 5*time.Minute +var FileCacheTimeout = 5 * time.Minute + // FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. var FileCacheSizeLimit = 1024 * 1024 + // fileResponseCache stores responses from the Gitea server // TODO: make this an MRU cache with a size limit var fileResponseCache = mcache.New() + type fileResponse struct { - exists bool + exists bool mimeType string - body []byte + body []byte } // getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch @@ -339,30 +345,30 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if branch == "" { // Get default branch var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 5 * time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 5*time.Second) if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, nil, DefaultBranchCacheTimeout) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) return nil } result.branch = fastjson.GetString(body, "default_branch") } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 5 * time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 5*time.Second) if err != nil || status != 200 { return nil } result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - _ = branchTimestampCache.Set(owner + "/" + repo + "/" + branch, result, BranchExistanceCacheTimeout) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) return result } var upstreamClient = fasthttp.Client{ - ReadTimeout: 10 * time.Second, - MaxConnDuration: 60 * time.Second, + ReadTimeout: 10 * time.Second, + MaxConnDuration: 60 * time.Second, MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! } // upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. @@ -426,7 +432,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range IndexPages { if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { - _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) return true @@ -436,7 +442,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Response.SetStatusCode(fasthttp.StatusNotFound) if res != nil { // Update cache if the request is fresh - _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) } @@ -496,7 +502,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) } return true diff --git a/handler_test.go b/handler_test.go index e7f51e7..70b655e 100644 --- a/handler_test.go +++ b/handler_test.go @@ -37,7 +37,6 @@ func TestHandlerPerformance(t *testing.T) { t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) } - ctx.Response.Reset() ctx.Response.ResetBody() ctx.Request.SetRequestURI("http://example.momar.xyz/") diff --git a/main.go b/main.go index 700241a..64f6447 100644 --- a/main.go +++ b/main.go @@ -92,7 +92,7 @@ func main() { NoDefaultServerHeader: true, NoDefaultDate: true, ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge - Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! + Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! MaxConnsPerIP: 100, } @@ -116,7 +116,7 @@ func main() { } ctx.SetBodyString(challenge.(string)) } else { - ctx.Redirect("https://" + string(ctx.Host()) + string(ctx.RequestURI()), http.StatusMovedPermanently) + ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) } }) if err != nil { From f2bb6aa36faf9df697c54bafadff1bb1fc35330f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Nov 2021 16:18:28 +0100 Subject: [PATCH 045/320] fix comment --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 53e11cc..cbfb958 100644 --- a/handler.go +++ b/handler.go @@ -298,7 +298,7 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) } -// BranchExistanceCacheTimeout specifies the timeout for the default branch cache. It can be quite long. +// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. var DefaultBranchCacheTimeout = 15 * time.Minute // BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter From a6da3eb5f069a1eb2785ed8cf39ec98bc0682595 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 25 Nov 2021 16:19:04 +0100 Subject: [PATCH 046/320] ignore vendor/ folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cb42378..3117f77 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ key-database.pogreb/ acme-account.json build/ +vendor/ From 772c17e21439762c66d16d605c4920e718b5641e Mon Sep 17 00:00:00 2001 From: fnetx Date: Fri, 26 Nov 2021 04:06:17 +0100 Subject: [PATCH 047/320] Pass Gitea API token to requests This allows to display repos that aren't fully public. Some users seem to be very interested in not having their pages viewable, and it might make even sense to avoid e.g. search engines to read them. If set to some random user string, this could allow to set the visibility at least to limited (so only logged users see the repo), and should allow to view private repos in the future with another API token. --- README.md | 1 + domains.go | 2 +- handler.go | 10 +++++----- main.go | 2 ++ 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f545228..b6b1b98 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. - `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. +- `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. - `REDIRECT_BROKEN_DNS` (default: https://docs.codeberg.org/pages/custom-domains/): info page for setting up DNS, shown for invalid DNS setups. - `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. - `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). diff --git a/domains.go b/domains.go index 4dcaaee..0699f0f 100644 --- a/domains.go +++ b/domains.go @@ -81,7 +81,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain st } } else { req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains") + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + string(GiteaApiToken)) res := fasthttp.AcquireResponse() err := upstreamClient.Do(req, res) diff --git a/handler.go b/handler.go index cbfb958..2df2096 100644 --- a/handler.go +++ b/handler.go @@ -152,7 +152,7 @@ func handler(ctx *fasthttp.RequestCtx) { if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { s.Step("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p"+"?access_token="+string(GiteaApiToken), ) { s.Step("tryBranch, now trying upstream") tryUpstream() @@ -164,7 +164,7 @@ func handler(ctx *fasthttp.RequestCtx) { } else { s.Step("raw domain preparations, now trying with default branch") tryBranch(targetRepo, "", pathElements[2:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p"+"?access_token="+string(GiteaApiToken), ) s.Step("tryBranch, now trying upstream") tryUpstream() @@ -345,7 +345,7 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if branch == "" { // Get default branch var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo, 5*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+string(GiteaApiToken), 5*time.Second) if err != nil || status != 200 { _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) return nil @@ -354,7 +354,7 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch, 5*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+string(GiteaApiToken), 5*time.Second) if err != nil || status != 200 { return nil } @@ -416,7 +416,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri) + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri + "?access_token=" + string(GiteaApiToken)) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) err = upstreamClient.Do(req, res) diff --git a/main.go b/main.go index 64f6447..331a81c 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,8 @@ var MainDomainSuffix = []byte("." + envOr("PAGES_DOMAIN", "codeberg.page")) // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. var GiteaRoot = []byte(envOr("GITEA_ROOT", "https://codeberg.org")) +var GiteaApiToken = []byte(envOr("GITEA_API_TOKEN", "")) + //go:embed 404.html var NotFoundPage []byte From 455f65216c6c3175c8f2a2dd96384bd5c1beed6b Mon Sep 17 00:00:00 2001 From: fnetx Date: Fri, 26 Nov 2021 17:03:58 +0100 Subject: [PATCH 048/320] Remove access token from canonicalLink as per momar --- handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index 2df2096..af6497d 100644 --- a/handler.go +++ b/handler.go @@ -152,7 +152,7 @@ func handler(ctx *fasthttp.RequestCtx) { if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { s.Step("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p"+"?access_token="+string(GiteaApiToken), + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { s.Step("tryBranch, now trying upstream") tryUpstream() @@ -164,7 +164,7 @@ func handler(ctx *fasthttp.RequestCtx) { } else { s.Step("raw domain preparations, now trying with default branch") tryBranch(targetRepo, "", pathElements[2:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p"+"?access_token="+string(GiteaApiToken), + string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) s.Step("tryBranch, now trying upstream") tryUpstream() From 73da80adc1b61712407a9f9d116300cbdf0f686f Mon Sep 17 00:00:00 2001 From: fnetx Date: Fri, 26 Nov 2021 17:10:31 +0100 Subject: [PATCH 049/320] Switch GiteaApiToken from byte to string --- domains.go | 2 +- handler.go | 6 +++--- main.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/domains.go b/domains.go index 0699f0f..0a5abc1 100644 --- a/domains.go +++ b/domains.go @@ -81,7 +81,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain st } } else { req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + string(GiteaApiToken)) + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + GiteaApiToken) res := fasthttp.AcquireResponse() err := upstreamClient.Do(req, res) diff --git a/handler.go b/handler.go index af6497d..720ccde 100644 --- a/handler.go +++ b/handler.go @@ -345,7 +345,7 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if branch == "" { // Get default branch var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+string(GiteaApiToken), 5*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+GiteaApiToken, 5*time.Second) if err != nil || status != 200 { _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) return nil @@ -354,7 +354,7 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { } var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+string(GiteaApiToken), 5*time.Second) + status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+GiteaApiToken, 5*time.Second) if err != nil || status != 200 { return nil } @@ -416,7 +416,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri + "?access_token=" + string(GiteaApiToken)) + req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri + "?access_token=" + GiteaApiToken) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) err = upstreamClient.Do(req, res) diff --git a/main.go b/main.go index 331a81c..1d73e33 100644 --- a/main.go +++ b/main.go @@ -39,7 +39,7 @@ var MainDomainSuffix = []byte("." + envOr("PAGES_DOMAIN", "codeberg.page")) // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. var GiteaRoot = []byte(envOr("GITEA_ROOT", "https://codeberg.org")) -var GiteaApiToken = []byte(envOr("GITEA_API_TOKEN", "")) +var GiteaApiToken = envOr("GITEA_API_TOKEN", "") //go:embed 404.html var NotFoundPage []byte From 6d520c2a40d9f6fe86f800a8371f9d4adad34001 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 1 Dec 2021 21:44:54 +0100 Subject: [PATCH 050/320] Update error message for private repo access --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 720ccde..a731412 100644 --- a/handler.go +++ b/handler.go @@ -293,7 +293,7 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { message += " - domain not specified in .domains file" } if code == fasthttp.StatusFailedDependency { - message += " - owner, repo or branch doesn't exist (if everything's set up correctly, wait up to 15 minutes for cache invalidation)" + message += " - target repo/branch doesn't exist or is private" } ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) } From f29ebc57d35210d0ae0abfdcfe438839e5e3b172 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 1 Dec 2021 16:23:37 +0100 Subject: [PATCH 051/320] Handle certificate errors with mock certificates (fixes #10) --- certificates.go | 263 +++++++++++++++++++++++++++++++++--------------- main.go | 19 ++++ 2 files changed, 200 insertions(+), 82 deletions(-) diff --git a/certificates.go b/certificates.go index b7c152f..f85109d 100644 --- a/certificates.go +++ b/certificates.go @@ -6,20 +6,23 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + "crypto/rsa" "crypto/tls" "crypto/x509" + "crypto/x509/pkix" "encoding/gob" "encoding/json" + "encoding/pem" "errors" "github.com/OrlovEvgeny/go-mcache" "github.com/akrylysov/pogreb/fs" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge" - "github.com/go-acme/lego/v4/challenge/resolver" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/providers/dns" "io/ioutil" "log" + "math/big" "os" "strings" "sync" @@ -127,7 +130,11 @@ var tlsConfig = &tls.Config{ } var keyCache = mcache.New() -var keyDatabase *pogreb.DB +var keyDatabase, keyDatabaseErr = pogreb.Open("key-database.pogreb", &pogreb.Options{ + BackgroundSyncInterval: 30 * time.Second, + BackgroundCompactionInterval: 6 * time.Hour, + FileSystem: fs.OSMMap, +}) func CheckUserLimit(user string) error { userLimit, ok := acmeClientCertificateLimitPerUser[user] @@ -162,18 +169,6 @@ func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { return u.Key } -func newAcmeClient(configureChallenge func(*resolver.SolverManager) error) *lego.Client { - acmeClient, err := lego.NewClient(myAcmeConfig) - if err != nil { - panic(err) - } - err = configureChallenge(acmeClient.Challenge) - if err != nil { - panic(err) - } - return acmeClient -} - var acmeClient, mainDomainAcmeClient *lego.Client var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} @@ -181,8 +176,8 @@ var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} // TODO: when this is used a lot, we probably have to think of a somewhat better solution? var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) -// rate limit is 20 / second, we want 10 / second -var acmeClientRequestLimit = equalizer.NewTokenBucket(10, 1*time.Second) +// rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) +var acmeClientRequestLimit = equalizer.NewTokenBucket(5, 1*time.Second) var challengeCache = mcache.New() @@ -277,16 +272,25 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } defer obtainLocks.Delete(name) + if acmeClient == nil { + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!"), nil + } + // request actual cert var res *certificate.Resource var err error - if renew != nil { + if renew != nil && renew.CertURL != "" { if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { acmeClientRequestLimit.Take() } log.Printf("Renewing certificate for %v", domains) res, err = acmeClient.Certificate.Renew(*renew, true, false, "") - } else { + if err != nil { + log.Printf("Couldn't renew certificate for %v, trying to request a new one: %s", domains, err) + res = nil + } + } + if res == nil { if user != "" { if err := CheckUserLimit(user); err != nil { return tls.Certificate{}, err @@ -306,7 +310,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } if err != nil { log.Printf("Couldn't obtain certificate for %v: %s", domains, err) - return tls.Certificate{}, err + return mockCert(domains[0], err.Error()), err } log.Printf("Obtained certificate for %v", domains) @@ -317,11 +321,6 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re panic(err) } err = keyDatabase.Put([]byte(name), resGob.Bytes()) - if err != nil { - _ = keyDatabase.Delete([]byte(name + "/key")) - obtainLocks.Delete(name) - panic(err) - } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -330,21 +329,97 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return tlsCertificate, nil } -func setupCertificates() { - var err error - keyDatabase, err = pogreb.Open("key-database.pogreb", &pogreb.Options{ - BackgroundSyncInterval: 30 * time.Second, - BackgroundCompactionInterval: 6 * time.Hour, - FileSystem: fs.OSMMap, - }) +func mockCert(domain string, msg string) tls.Certificate { + key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) if err != nil { panic(err) } + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: domain, + Organization: []string{"Codeberg Pages Error Certificate (couldn't obtain ACME certificate)"}, + OrganizationalUnit: []string{ + "Will not try again for 6 hours to avoid hitting rate limits for your domain.", + "Check https://docs.codeberg.org/codeberg-pages/troubleshooting/ for troubleshooting tips, and feel " + + "free to create an issue at https://codeberg.org/Codeberg/pages-server if you can't solve it.\n", + "Error message: " + msg, + }, + }, + + // certificates younger than 7 days are renewed, so this enforces the cert to not be renewed for a 6 hours + NotAfter: time.Now().Add(time.Hour * 24 * 7 + time.Hour * 6), + NotBefore: time.Now(), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate( + rand.Reader, + &template, + &template, + &key.(*rsa.PrivateKey).PublicKey, + key, + ) + if err != nil { + panic(err) + } + + out := &bytes.Buffer{} + err = pem.Encode(out, &pem.Block{ + Bytes: certBytes, + Type: "CERTIFICATE", + }) + if err != nil { + panic(err) + } + outBytes := out.Bytes() + res := &certificate.Resource{ + PrivateKey: certcrypto.PEMEncode(key), + Certificate: outBytes, + IssuerCertificate: outBytes, + Domain: domain, + } + var resGob bytes.Buffer + resEnc := gob.NewEncoder(&resGob) + err = resEnc.Encode(res) + if err != nil { + panic(err) + } + databaseName := domain + if domain == "*" + string(MainDomainSuffix) || domain == string(MainDomainSuffix[1:]) { + databaseName = string(MainDomainSuffix) + } + err = keyDatabase.Put([]byte(databaseName), resGob.Bytes()) + if err != nil { + panic(err) + } + + tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) + if err != nil { + panic(err) + } + return tlsCertificate +} + +func setupCertificates() { + if keyDatabaseErr != nil { + panic(keyDatabaseErr) + } + if os.Getenv("ACME_ACCEPT_TERMS") != "true" || (os.Getenv("DNS_PROVIDER") == "" && os.Getenv("ACME_API") != "https://acme.mock.directory") { panic(errors.New("you must set ACME_ACCEPT_TERMS and DNS_PROVIDER, unless ACME_API is set to https://acme.mock.directory")) } + // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits + mainCertBytes, err := keyDatabase.Get(MainDomainSuffix) + if err != nil { + // key database is not working + panic(err) + } + if account, err := ioutil.ReadFile("acme-account.json"); err == nil { err = json.Unmarshal(account, &myAcmeAccount) if err != nil { @@ -357,7 +432,10 @@ func setupCertificates() { myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) + _, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + } } else if os.IsNotExist(err) { privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -371,69 +449,90 @@ func setupCertificates() { myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - tempClient := newAcmeClient(func(manager *resolver.SolverManager) error { return nil }) - - // accept terms & log in to EAB - if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) - if err != nil { - panic(err) - } - myAcmeAccount.Registration = reg + tempClient, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { - reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", - Kid: os.Getenv("ACME_EAB_KID"), - HmacEncoded: os.Getenv("ACME_EAB_HMAC"), - }) - if err != nil { - panic(err) + // accept terms & log in to EAB + if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if err != nil { + log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + } else { + myAcmeAccount.Registration = reg + } + } else { + reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", + Kid: os.Getenv("ACME_EAB_KID"), + HmacEncoded: os.Getenv("ACME_EAB_HMAC"), + }) + if err != nil { + log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + } else { + myAcmeAccount.Registration = reg + } } - myAcmeAccount.Registration = reg - } - acmeAccountJson, err := json.Marshal(myAcmeAccount) - if err != nil { - panic(err) - } - err = ioutil.WriteFile("acme-account.json", acmeAccountJson, 0600) - if err != nil { - panic(err) + if myAcmeAccount.Registration != nil { + acmeAccountJson, err := json.Marshal(myAcmeAccount) + if err != nil { + log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) + select {} + } + err = ioutil.WriteFile("acme-account.json", acmeAccountJson, 0600) + if err != nil { + log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) + select {} + } + } } } else { panic(err) } - acmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { - err = challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + acmeClient, err = lego.NewClient(myAcmeConfig) + if err != nil { + log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + } else { + err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) if err != nil { - return err + log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) } if os.Getenv("ENABLE_HTTP_SERVER") == "true" { - return challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{}) + err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{}) + if err != nil { + log.Printf("[ERROR] Can't create HTTP-01 provider: %s", err) + } } - return err - }) - mainDomainAcmeClient = newAcmeClient(func(challenge *resolver.SolverManager) error { + } + + mainDomainAcmeClient, err = lego.NewClient(myAcmeConfig) + if err != nil { + log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + } else { if os.Getenv("DNS_PROVIDER") == "" { // using mock server, don't use wildcard certs - return challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + if err != nil { + log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) + } + } else { + provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) + if err != nil { + log.Printf("[ERROR] Can't create DNS Challenge provider: %s", err) + } + err = mainDomainAcmeClient.Challenge.SetDNS01Provider(provider) + if err != nil { + log.Printf("[ERROR] Can't create DNS-01 provider: %s", err) + } } - provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) - if err != nil { - return err - } - return challenge.SetDNS01Provider(provider) - }) + } - resBytes, err := keyDatabase.Get(MainDomainSuffix) - if err != nil { - // key database is not working - panic(err) - } else if resBytes == nil { + if mainCertBytes == nil { _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, nil, "") if err != nil { - log.Fatalf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) + log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } } @@ -441,7 +540,7 @@ func setupCertificates() { for { err := keyDatabase.Sync() if err != nil { - log.Printf("Syncinc key database failed: %s", err) + log.Printf("[ERROR] Syncinc key database failed: %s", err) } time.Sleep(5 * time.Minute) } @@ -467,7 +566,7 @@ func setupCertificates() { if err != nil || !tlsCertificates[0].NotAfter.After(now) { err := keyDatabase.Delete(key) if err != nil { - log.Printf("Deleting expired certificate for %s failed: %s", string(key), err) + log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) } else { expiredCertCount++ } @@ -475,14 +574,14 @@ func setupCertificates() { } key, resBytes, err = keyDatabaseIterator.Next() } - log.Printf("Removed %d expired certificates from the database", expiredCertCount) + log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) // compact the database result, err := keyDatabase.Compact() if err != nil { - log.Printf("Compacting key database failed: %s", err) + log.Printf("[ERROR] Compacting key database failed: %s", err) } else { - log.Printf("Compacted key database (%+v)", result) + log.Printf("[INFO] Compacted key database (%+v)", result) } // update main cert diff --git a/main.go b/main.go index 1d73e33..d6a1d7f 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,25 @@ var IndexPages = []string{ // main sets up and starts the web server. func main() { + if len(os.Args) > 1 && os.Args[1] == "--remove-certificate" { + if len(os.Args) < 2 { + println("--remove-certificate requires at least one domain as an argument") + os.Exit(1) + } + if keyDatabaseErr != nil { + panic(keyDatabaseErr) + } + for _, domain := range os.Args[2:] { + if err := keyDatabase.Delete([]byte(domain)); err != nil { + panic(err) + } + } + if err := keyDatabase.Sync(); err != nil { + panic(err) + } + os.Exit(0) + } + // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash if !bytes.HasPrefix(MainDomainSuffix, []byte{'.'}) { MainDomainSuffix = append([]byte{'.'}, MainDomainSuffix...) From 544b3f73217522ff28c6f4ed99470290f60749d4 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 1 Dec 2021 22:49:48 +0100 Subject: [PATCH 052/320] (Ab)use CSR field to store try-again date for renewals (instead of showing a mock cert), must be tested when the first renewals are due --- certificates.go | 92 ++++++++++++++++++++----------------------------- helpers.go | 37 +++++++++++++++++++- 2 files changed, 73 insertions(+), 56 deletions(-) diff --git a/certificates.go b/certificates.go index f85109d..36f0df0 100644 --- a/certificates.go +++ b/certificates.go @@ -24,6 +24,7 @@ import ( "log" "math/big" "os" + "strconv" "strings" "sync" "time" @@ -207,21 +208,9 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // parse certificate from database - resBytes, err := keyDatabase.Get(sni) - if err != nil { - // key database is not working - panic(err) - } - if resBytes == nil { - return tls.Certificate{}, false - } - - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) res := &certificate.Resource{} - err = resDec.Decode(res) - if err != nil { - panic(err) + if !PogrebGet(keyDatabase, sni, res) { + return tls.Certificate{}, false } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) @@ -237,7 +226,15 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // renew certificates 7 days before they expire if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { + if res.CSR != nil && len(res.CSR) > 0 { + // CSR stores the time when the renewal shall be tried again + nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64) + if err == nil && time.Now().Before(time.Unix(nextTryUnix, 0)) { + return tlsCertificate, true + } + } go (func() { + res.CSR = nil // acme client doesn't like CSR to be set tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "") if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) @@ -310,18 +307,21 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } if err != nil { log.Printf("Couldn't obtain certificate for %v: %s", domains, err) - return mockCert(domains[0], err.Error()), err + if renew != nil && renew.CertURL != "" { + tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) + if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { + // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at + renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6 * time.Hour).Unix(), 10)) + PogrebPut(keyDatabase, []byte(name), renew) + return tlsCertificate, nil + } + } else { + return mockCert(domains[0], err.Error()), err + } } log.Printf("Obtained certificate for %v", domains) - var resGob bytes.Buffer - resEnc := gob.NewEncoder(&resGob) - err = resEnc.Encode(res) - if err != nil { - panic(err) - } - err = keyDatabase.Put([]byte(name), resGob.Bytes()) - + PogrebPut(keyDatabase, []byte(name), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -382,20 +382,11 @@ func mockCert(domain string, msg string) tls.Certificate { IssuerCertificate: outBytes, Domain: domain, } - var resGob bytes.Buffer - resEnc := gob.NewEncoder(&resGob) - err = resEnc.Encode(res) - if err != nil { - panic(err) - } databaseName := domain if domain == "*" + string(MainDomainSuffix) || domain == string(MainDomainSuffix[1:]) { databaseName = string(MainDomainSuffix) } - err = keyDatabase.Put([]byte(databaseName), resGob.Bytes()) - if err != nil { - panic(err) - } + PogrebPut(keyDatabase, []byte(databaseName), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -585,30 +576,21 @@ func setupCertificates() { } // update main cert - resBytes, err = keyDatabase.Get(MainDomainSuffix) - if err != nil { - // key database is not working - panic(err) - } - - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) res := &certificate.Resource{} - err = resDec.Decode(res) - if err != nil { - panic(err) - } + if !PogrebGet(keyDatabase, MainDomainSuffix, res) { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") + } else { + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - - // renew main certificate 30 days before it expires - if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { - go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res, "") - if err != nil { - log.Printf("Couldn't renew certificate for *%s: %s", MainDomainSuffix, err) - } - })() + // renew main certificate 30 days before it expires + if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { + go (func() { + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res, "") + if err != nil { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) + } + })() + } } time.Sleep(12 * time.Hour) diff --git a/helpers.go b/helpers.go index 506573b..46a1492 100644 --- a/helpers.go +++ b/helpers.go @@ -1,6 +1,10 @@ package main -import "bytes" +import ( + "bytes" + "encoding/gob" + "github.com/akrylysov/pogreb" +) // GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty // string for custom domains. @@ -19,3 +23,34 @@ func TrimHostPort(host []byte) []byte { } return host } + +func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { + var resGob bytes.Buffer + resEnc := gob.NewEncoder(&resGob) + err := resEnc.Encode(obj) + if err != nil { + panic(err) + } + err = db.Put(name, resGob.Bytes()) + if err != nil { + panic(err) + } +} + +func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { + resBytes, err := db.Get(name) + if err != nil { + panic(err) + } + if resBytes == nil { + return false + } + + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + err = resDec.Decode(obj) + if err != nil { + panic(err) + } + return true +} From fedfa8def462dc14f32b85a0556af36dd2f2c633 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 1 Dec 2021 22:59:38 +0100 Subject: [PATCH 053/320] Remove REDIRECT_BROKEN_DNS page and instead use "Failed Dependency" error --- README.md | 1 - handler.go | 4 ++-- main.go | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b6b1b98..35c230e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ - `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. -- `REDIRECT_BROKEN_DNS` (default: https://docs.codeberg.org/pages/custom-domains/): info page for setting up DNS, shown for invalid DNS setups. - `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. - `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. diff --git a/handler.go b/handler.go index a731412..f563b14 100644 --- a/handler.go +++ b/handler.go @@ -242,7 +242,7 @@ func handler(ctx *fasthttp.RequestCtx) { // Serve pages from external domains targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr) if targetOwner == "" { - ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) + returnErrorPage(ctx, fasthttp.StatusFailedDependency) return } @@ -268,7 +268,7 @@ func handler(ctx *fasthttp.RequestCtx) { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return } else { - ctx.Redirect(BrokenDNSPage, fasthttp.StatusTemporaryRedirect) + returnErrorPage(ctx, fasthttp.StatusFailedDependency) return } } diff --git a/main.go b/main.go index d6a1d7f..44cec0f 100644 --- a/main.go +++ b/main.go @@ -44,9 +44,6 @@ var GiteaApiToken = envOr("GITEA_API_TOKEN", "") //go:embed 404.html var NotFoundPage []byte -// BrokenDNSPage will be shown (with a redirect) when trying to access a domain for which no DNS CNAME record exists. -var BrokenDNSPage = envOr("REDIRECT_BROKEN_DNS", "https://docs.codeberg.org/pages/custom-domains/") - // RawDomain specifies the domain from which raw repository content shall be served in the following format: // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} // (set to []byte(nil) to disable raw content hosting) From a48ba8ee49932353fa115899d2e81406d6d0d14e Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Wed, 1 Dec 2021 22:59:52 +0100 Subject: [PATCH 054/320] Run gofmt --- certificates.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/certificates.go b/certificates.go index 36f0df0..db51020 100644 --- a/certificates.go +++ b/certificates.go @@ -311,7 +311,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at - renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6 * time.Hour).Unix(), 10)) + renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) PogrebPut(keyDatabase, []byte(name), renew) return tlsCertificate, nil } @@ -338,7 +338,7 @@ func mockCert(domain string, msg string) tls.Certificate { template := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ - CommonName: domain, + CommonName: domain, Organization: []string{"Codeberg Pages Error Certificate (couldn't obtain ACME certificate)"}, OrganizationalUnit: []string{ "Will not try again for 6 hours to avoid hitting rate limits for your domain.", @@ -349,7 +349,7 @@ func mockCert(domain string, msg string) tls.Certificate { }, // certificates younger than 7 days are renewed, so this enforces the cert to not be renewed for a 6 hours - NotAfter: time.Now().Add(time.Hour * 24 * 7 + time.Hour * 6), + NotAfter: time.Now().Add(time.Hour*24*7 + time.Hour*6), NotBefore: time.Now(), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, @@ -370,20 +370,20 @@ func mockCert(domain string, msg string) tls.Certificate { out := &bytes.Buffer{} err = pem.Encode(out, &pem.Block{ Bytes: certBytes, - Type: "CERTIFICATE", + Type: "CERTIFICATE", }) if err != nil { panic(err) } outBytes := out.Bytes() res := &certificate.Resource{ - PrivateKey: certcrypto.PEMEncode(key), - Certificate: outBytes, + PrivateKey: certcrypto.PEMEncode(key), + Certificate: outBytes, IssuerCertificate: outBytes, - Domain: domain, + Domain: domain, } databaseName := domain - if domain == "*" + string(MainDomainSuffix) || domain == string(MainDomainSuffix[1:]) { + if domain == "*"+string(MainDomainSuffix) || domain == string(MainDomainSuffix[1:]) { databaseName = string(MainDomainSuffix) } PogrebPut(keyDatabase, []byte(databaseName), res) From 26dd1591f6a8a929b9c93df27a59b5402c9c7162 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 2 Dec 2021 00:00:00 +0100 Subject: [PATCH 055/320] Add www redirect for main domain --- handler.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/handler.go b/handler.go index f563b14..44865ac 100644 --- a/handler.go +++ b/handler.go @@ -180,6 +180,12 @@ func handler(ctx *fasthttp.RequestCtx) { targetRepo = pathElements[0] targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") + if targetOwner == "www" { + // www.codeberg.page redirects to codeberg.page + ctx.Redirect("https://" + string(MainDomainSuffix[1:]) + string(ctx.Path()), fasthttp.StatusPermanentRedirect) + return + } + // Check if the first directory is a repo with the second directory as a branch // example.codeberg.page/myrepo/@main/index.html if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { From 989d00832fbceaf2bf18da3a91c4df3acd042fce Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 2 Dec 2021 19:11:13 +0100 Subject: [PATCH 056/320] Fix (half) empty cache issue --- handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index 44865ac..675c96a 100644 --- a/handler.go +++ b/handler.go @@ -418,7 +418,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t var res *fasthttp.Response var cachedResponse fileResponse var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok { + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() @@ -504,7 +504,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t } s.Step("response") - if res != nil { + if res != nil && ctx.Err() == nil { cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() From b6d7f5a6ee437d978b2b756c014ff48143e31217 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 2 Dec 2021 10:23:03 +0100 Subject: [PATCH 057/320] Change browser cache to 10 minutes to make bigger pages more performant --- handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handler.go b/handler.go index 675c96a..3ebbf29 100644 --- a/handler.go +++ b/handler.go @@ -25,8 +25,8 @@ func handler(ctx *fasthttp.RequestCtx) { // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") - // Enable caching, but require revalidation to reduce confusion - ctx.Response.Header.Set("Cache-Control", "must-revalidate") + // Enable browser caching for up to 10 minutes + ctx.Response.Header.Set("Cache-Control", "public, max-age=600") trimmedHost := TrimHostPort(ctx.Request.Host()) From 57dce3b0c5e28297a9194e1ed7509752906a6bb3 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 2 Dec 2021 10:16:23 +0100 Subject: [PATCH 058/320] Add redirect for GitHub-style non-".html" paths & force remove index.html suffix See https://codeberg.org/Codeberg/Community/issues/547 for more info --- handler.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 3ebbf29..4647342 100644 --- a/handler.go +++ b/handler.go @@ -444,6 +444,15 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t return true } } + // compatibility fix for GitHub Pages (/example → /example.html) + optionsForIndexPages.AppendTrailingSlash = false + optionsForIndexPages.RedirectIfExists = targetPath + ".html" + if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath + ".html", &optionsForIndexPages) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + return true + } } ctx.Response.SetStatusCode(fasthttp.StatusNotFound) if res != nil { @@ -460,12 +469,20 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t return true } - // Append trailing slash if missing (for index files) + // Append trailing slash if missing (for index files), and redirect to fix filenames in general // options.AppendTrailingSlash is only true when looking for index pages if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) return true } + if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { + ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) + return true + } + if options.RedirectIfExists != "" { + ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) + return true + } s.Step("error handling") // Set the MIME type @@ -520,5 +537,6 @@ type upstreamOptions struct { ForbiddenMimeTypes map[string]struct{} TryIndexPages bool AppendTrailingSlash bool + RedirectIfExists string BranchTimestamp time.Time } From 51c79f512d0c4972b6acf328035ab504619860b6 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 4 Dec 2021 13:54:18 +0100 Subject: [PATCH 059/320] Fix github-style non-.html URLs repeating the path twice Issue was reported in https://codeberg.org/Codeberg/Community/issues/547#issuecomment-285075 --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 4647342..626385d 100644 --- a/handler.go +++ b/handler.go @@ -446,7 +446,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t } // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.AppendTrailingSlash = false - optionsForIndexPages.RedirectIfExists = targetPath + ".html" + optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath + ".html", &optionsForIndexPages) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, From 76e5d8e77c1636d2cadbb48cb6176d8231aa69bf Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 2 Dec 2021 19:12:45 +0100 Subject: [PATCH 060/320] Add TODOs --- certificates.go | 9 ++++++--- handler.go | 3 +++ haproxy-sni/haproxy.cfg | 1 + main.go | 4 +++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/certificates.go b/certificates.go index db51020..73b4793 100644 --- a/certificates.go +++ b/certificates.go @@ -130,6 +130,7 @@ var tlsConfig = &tls.Config{ }, } +// TODO: clean up & move to init var keyCache = mcache.New() var keyDatabase, keyDatabaseErr = pogreb.Open("key-database.pogreb", &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, @@ -218,6 +219,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { panic(err) } + // TODO: document & put into own function if !bytes.Equal(sni, MainDomainSuffix) { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { @@ -226,6 +228,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { // renew certificates 7 days before they expire if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { + // TODO: add ValidUntil to custom res struct if res.CSR != nil && len(res.CSR) > 0 { // CSR stores the time when the renewal shall be tried again nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64) @@ -315,9 +318,8 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re PogrebPut(keyDatabase, []byte(name), renew) return tlsCertificate, nil } - } else { - return mockCert(domains[0], err.Error()), err } + return mockCert(domains[0], err.Error()), err } log.Printf("Obtained certificate for %v", domains) @@ -531,9 +533,10 @@ func setupCertificates() { for { err := keyDatabase.Sync() if err != nil { - log.Printf("[ERROR] Syncinc key database failed: %s", err) + log.Printf("[ERROR] Syncing key database failed: %s", err) } time.Sleep(5 * time.Minute) + // TODO: graceful exit } })() go (func() { diff --git a/handler.go b/handler.go index 626385d..4e9efbf 100644 --- a/handler.go +++ b/handler.go @@ -301,6 +301,7 @@ func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { if code == fasthttp.StatusFailedDependency { message += " - target repo/branch doesn't exist or is private" } + // TODO: use template engine? ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) } @@ -351,6 +352,7 @@ func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { if branch == "" { // Get default branch var body = make([]byte, 0) + // TODO: use header for API key? status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+GiteaApiToken, 5*time.Second) if err != nil || status != 200 { _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) @@ -509,6 +511,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t if res.Header.ContentLength() > FileCacheSizeLimit { err = res.BodyWriteTo(ctx.Response.BodyWriter()) } else { + // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) } } else { diff --git a/haproxy-sni/haproxy.cfg b/haproxy-sni/haproxy.cfg index 869bae3..c8f3610 100644 --- a/haproxy-sni/haproxy.cfg +++ b/haproxy-sni/haproxy.cfg @@ -51,6 +51,7 @@ frontend https_sni_frontend ################################################### acl use_http_backend req.ssl_sni -i "codeberg.org" acl use_http_backend req.ssl_sni -i "join.codeberg.org" + # TODO: use this if no SNI exists use_backend https_termination_backend if use_http_backend ############################ diff --git a/main.go b/main.go index 44cec0f..1a4cb65 100644 --- a/main.go +++ b/main.go @@ -71,6 +71,7 @@ var IndexPages = []string{ // main sets up and starts the web server. func main() { + // TODO: CLI Library if len(os.Args) > 1 && os.Args[1] == "--remove-certificate" { if len(os.Args) < 2 { println("--remove-certificate requires at least one domain as an argument") @@ -105,7 +106,7 @@ func main() { server := &fasthttp.Server{ Handler: compressedHandler, - DisablePreParseMultipartForm: false, + DisablePreParseMultipartForm: true, MaxRequestBodySize: 0, NoDefaultServerHeader: true, NoDefaultDate: true, @@ -151,6 +152,7 @@ func main() { } // envOr reads an environment variable and returns a default value if it's empty. +// TODO: to helpers.go or use CLI framework func envOr(env string, or string) string { if v := os.Getenv(env); v != "" { return v From fb5726bd202e40a3de9e113d290e8ffb7100396a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 14:43:51 +0100 Subject: [PATCH 061/320] use zerolog instead of own logger --- debug-stepper/stepper.go | 68 ---------------------------------------- go.mod | 1 + go.sum | 10 ++++++ handler.go | 67 +++++++++++++++++++-------------------- 4 files changed, 44 insertions(+), 102 deletions(-) delete mode 100644 debug-stepper/stepper.go diff --git a/debug-stepper/stepper.go b/debug-stepper/stepper.go deleted file mode 100644 index 05506b6..0000000 --- a/debug-stepper/stepper.go +++ /dev/null @@ -1,68 +0,0 @@ -package debug_stepper - -import ( - "fmt" - "os" - "strings" - "time" -) - -var Enabled = strings.HasSuffix(os.Args[0], ".test") || os.Getenv("DEBUG") == "1" - -var Logger = func(s string, i ...interface{}) { - fmt.Printf(s, i...) -} - -type Stepper struct { - Name string - Start time.Time - LastStep time.Time - Completion time.Time -} - -func Start(name string) *Stepper { - if !Enabled { - return nil - } - t := time.Now() - Logger("%s: started at %s\n", name, t.Format(time.RFC3339)) - return &Stepper{ - Name: name, - Start: t, - LastStep: t, - } -} - -func (s *Stepper) Debug(text string) { - if !Enabled { - return - } - t := time.Now() - Logger("%s: %s (at %s, %s since last step, %s since start)\n", s.Name, text, t.Format(time.RFC3339), t.Sub(s.LastStep).String(), t.Sub(s.Start).String()) -} - -func (s *Stepper) Step(description string) { - if !Enabled { - return - } - if s.Completion != (time.Time{}) { - Logger("%s: already completed all tasks.\n") - return - } - t := time.Now() - Logger("%s: completed %s at %s (%s)\n", s.Name, description, t.Format(time.RFC3339), t.Sub(s.LastStep).String()) - s.LastStep = t -} - -func (s *Stepper) Complete() { - if !Enabled { - return - } - if s.Completion != (time.Time{}) { - Logger("%s: already completed all tasks.\n") - return - } - t := time.Now() - Logger("%s: completed all tasks at %s (%s since last step; total time: %s)\n", s.Name, t.Format(time.RFC3339), t.Sub(s.LastStep).String(), t.Sub(s.Start).String()) - s.Completion = t -} diff --git a/go.mod b/go.mod index fd52ef1..7a64921 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad + github.com/rs/zerolog v1.26.0 // indirect github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index c93f985..8561c5c 100644 --- a/go.sum +++ b/go.sum @@ -95,6 +95,7 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= @@ -148,6 +149,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -422,6 +424,9 @@ github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjI github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= +github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= @@ -499,6 +504,7 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -557,6 +563,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -591,6 +598,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -655,6 +663,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -708,6 +717,7 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/handler.go b/handler.go index 4e9efbf..55ae36d 100644 --- a/handler.go +++ b/handler.go @@ -2,23 +2,23 @@ package main import ( "bytes" - debug_stepper "codeberg.org/codeberg/pages/debug-stepper" "fmt" - "github.com/OrlovEvgeny/go-mcache" - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" "io" "mime" "path" "strconv" "strings" "time" + + "github.com/OrlovEvgeny/go-mcache" + "github.com/rs/zerolog/log" + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" ) // handler handles a single HTTP request to the web server. func handler(ctx *fasthttp.RequestCtx) { - s := debug_stepper.Start("handler") - defer s.Complete() + log := log.With().Str("handler", string(ctx.Request.Header.RequestURI())).Logger() ctx.Response.Header.Set("Server", "Codeberg Pages") @@ -129,11 +129,11 @@ func handler(ctx *fasthttp.RequestCtx) { } } - s.Step("preparations") + log.Debug().Msg("preparations") if RawDomain != nil && bytes.Equal(trimmedHost, RawDomain) { // Serve raw content from RawDomain - s.Debug("raw domain") + log.Debug().Msg("raw domain") targetOptions.TryIndexPages = false targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} @@ -150,30 +150,30 @@ func handler(ctx *fasthttp.RequestCtx) { // raw.codeberg.org/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { - s.Step("raw domain preparations, now trying with specified branch") + log.Debug().Msg("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() return } - s.Debug("missing branch") + log.Debug().Msg("missing branch") returnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { - s.Step("raw domain preparations, now trying with default branch") + log.Debug().Msg("raw domain preparations, now trying with default branch") tryBranch(targetRepo, "", pathElements[2:], string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() return } } else if bytes.HasSuffix(trimmedHost, MainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix - s.Debug("main domain suffix") + log.Debug().Msg("main domain suffix") pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") targetOwner = string(bytes.TrimSuffix(trimmedHost, MainDomainSuffix)) @@ -182,7 +182,7 @@ func handler(ctx *fasthttp.RequestCtx) { if targetOwner == "www" { // www.codeberg.page redirects to codeberg.page - ctx.Redirect("https://" + string(MainDomainSuffix[1:]) + string(ctx.Path()), fasthttp.StatusPermanentRedirect) + ctx.Redirect("https://"+string(MainDomainSuffix[1:])+string(ctx.Path()), fasthttp.StatusPermanentRedirect) return } @@ -195,11 +195,11 @@ func handler(ctx *fasthttp.RequestCtx) { return } - s.Step("main domain preparations, now trying with specified repo & branch") + log.Debug().Msg("main domain preparations, now trying with specified repo & branch") if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() } else { returnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -210,9 +210,9 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a branch for the "pages" repo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { - s.Step("main domain preparations, now trying with specified branch") + log.Debug().Msg("main domain preparations, now trying with specified branch") if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() } else { returnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -223,18 +223,18 @@ func handler(ctx *fasthttp.RequestCtx) { // Check if the first directory is a repo with a "pages" branch // example.codeberg.page/myrepo/index.html // example.codeberg.page/pages/... is not allowed here. - s.Step("main domain preparations, now trying with specified repo") + log.Debug().Msg("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() return } // Try to use the "pages" repo on its default branch // example.codeberg.page/index.html - s.Step("main domain preparations, now trying with default repo/branch") + log.Debug().Msg("main domain preparations, now trying with default repo/branch") if tryBranch("pages", "", pathElements, "") { - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() return } @@ -261,7 +261,7 @@ func handler(ctx *fasthttp.RequestCtx) { } // Try to use the given repo on the given branch or the default branch - s.Step("custom domain preparations, now trying with details from DNS") + log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr) if !valid { @@ -279,7 +279,7 @@ func handler(ctx *fasthttp.RequestCtx) { } } - s.Step("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() return } else { @@ -380,9 +380,8 @@ var upstreamClient = fasthttp.Client{ } // upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, targetBranch string, targetPath string, options *upstreamOptions) (final bool) { - s := debug_stepper.Start("upstream") - defer s.Complete() +func upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath string, options *upstreamOptions) (final bool) { + log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() if options.ForbiddenMimeTypes == nil { options.ForbiddenMimeTypes = map[string]struct{}{} @@ -412,7 +411,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t return true } } - s.Step("preparations") + log.Debug().Msg("preparations") // Make a GET request to the upstream URL uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath @@ -429,7 +428,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t res.SetBodyStream(&strings.Reader{}, -1) err = upstreamClient.Do(req, res) } - s.Step("acquisition") + log.Debug().Msg("acquisition") // Handle errors if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { @@ -449,7 +448,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.AppendTrailingSlash = false optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath + ".html", &optionsForIndexPages) { + if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", &optionsForIndexPages) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) @@ -485,7 +484,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) return true } - s.Step("error handling") + log.Debug().Msg("error handling") // Set the MIME type mimeType := mime.TypeByExtension(path.Ext(targetPath)) @@ -503,7 +502,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t ctx.Response.SetStatusCode(fasthttp.StatusOK) ctx.Response.Header.SetLastModified(options.BranchTimestamp) - s.Step("response preparations") + log.Debug().Msg("response preparations") // Write the response body to the original request var cacheBodyWriter bytes.Buffer @@ -522,7 +521,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t returnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } - s.Step("response") + log.Debug().Msg("response") if res != nil && ctx.Err() == nil { cachedResponse.exists = true From bdc2d0c2592c8c81358eef562a9eb1266f9aaf56 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 14:45:17 +0100 Subject: [PATCH 062/320] dont access global vars inject them --- go.mod | 2 +- go.sum | 4 +- handler.go | 544 --------------------- 404.html => html/404.html | 0 html/html.go | 6 + main.go | 52 +- certificates.go => server/certificates.go | 224 ++++----- domains.go => server/domains.go | 16 +- server/handler.go | 553 ++++++++++++++++++++++ handler_test.go => server/handler_test.go | 18 +- helpers.go => server/helpers.go | 17 +- 11 files changed, 730 insertions(+), 706 deletions(-) delete mode 100644 handler.go rename 404.html => html/404.html (100%) create mode 100644 html/html.go rename certificates.go => server/certificates.go (74%) rename domains.go => server/domains.go (83%) create mode 100644 server/handler.go rename handler_test.go => server/handler_test.go (80%) rename helpers.go => server/helpers.go (71%) diff --git a/go.mod b/go.mod index 7a64921..55e4675 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad - github.com/rs/zerolog v1.26.0 // indirect + github.com/rs/zerolog v1.26.0 github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index 8561c5c..65da291 100644 --- a/go.sum +++ b/go.sum @@ -596,8 +596,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -661,8 +661,8 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/handler.go b/handler.go deleted file mode 100644 index 55ae36d..0000000 --- a/handler.go +++ /dev/null @@ -1,544 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io" - "mime" - "path" - "strconv" - "strings" - "time" - - "github.com/OrlovEvgeny/go-mcache" - "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" -) - -// handler handles a single HTTP request to the web server. -func handler(ctx *fasthttp.RequestCtx) { - log := log.With().Str("handler", string(ctx.Request.Header.RequestURI())).Logger() - - ctx.Response.Header.Set("Server", "Codeberg Pages") - - // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin - ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") - - // Enable browser caching for up to 10 minutes - ctx.Response.Header.Set("Cache-Control", "public, max-age=600") - - trimmedHost := TrimHostPort(ctx.Request.Host()) - - // Add HSTS for RawDomain and MainDomainSuffix - if hsts := GetHSTSHeader(trimmedHost); hsts != "" { - ctx.Response.Header.Set("Strict-Transport-Security", hsts) - } - - // Block all methods not required for static pages - if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) - return - } - - // Block blacklisted paths (like ACME challenges) - for _, blacklistedPath := range BlacklistedPaths { - if bytes.HasPrefix(ctx.Path(), blacklistedPath) { - returnErrorPage(ctx, fasthttp.StatusForbidden) - return - } - } - - // Allow CORS for specified domains - if ctx.IsOptions() { - allowCors := false - for _, allowedCorsDomain := range AllowedCorsDomains { - if bytes.Equal(trimmedHost, allowedCorsDomain) { - allowCors = true - break - } - } - if allowCors { - ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") - ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") - } - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) - return - } - - // Prepare request information to Gitea - var targetOwner, targetRepo, targetBranch, targetPath string - var targetOptions = &upstreamOptions{ - ForbiddenMimeTypes: map[string]struct{}{}, - TryIndexPages: true, - } - - // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will - // also disallow search indexing and add a Link header to the canonical URL. - var tryBranch = func(repo string, branch string, path []string, canonicalLink string) bool { - if repo == "" { - return false - } - - // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch) - if branchTimestampResult == nil { - // branch doesn't exist - return false - } - - // Branch exists, use it - targetRepo = repo - targetPath = strings.Trim(strings.Join(path, "/"), "/") - targetBranch = branchTimestampResult.branch - - targetOptions.BranchTimestamp = branchTimestampResult.timestamp - - if canonicalLink != "" { - // Hide from search machines & add canonical link - ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") - ctx.Response.Header.Set("Link", - strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ - "; rel=\"canonical\"", - ) - } - - return true - } - - // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. - var tryUpstream = func() { - // check if a canonical domain exists on a request on MainDomain - if bytes.HasSuffix(trimmedHost, MainDomainSuffix) { - canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "") - if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(MainDomainSuffix)) { - canonicalPath := string(ctx.RequestURI()) - if targetRepo != "pages" { - canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] - } - ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) - return - } - } - - // Try to request the file from the Gitea API - if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, targetOptions) { - returnErrorPage(ctx, ctx.Response.StatusCode()) - } - } - - log.Debug().Msg("preparations") - - if RawDomain != nil && bytes.Equal(trimmedHost, RawDomain) { - // Serve raw content from RawDomain - log.Debug().Msg("raw domain") - - targetOptions.TryIndexPages = false - targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} - targetOptions.DefaultMimeType = "text/plain; charset=utf-8" - - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") - if len(pathElements) < 2 { - // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required - ctx.Redirect(RawInfoPage, fasthttp.StatusTemporaryRedirect) - return - } - targetOwner = pathElements[0] - targetRepo = pathElements[1] - - // raw.codeberg.org/example/myrepo/@main/index.html - if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { - log.Debug().Msg("raw domain preparations, now trying with specified branch") - if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", - ) { - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - return - } - log.Debug().Msg("missing branch") - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return - } else { - log.Debug().Msg("raw domain preparations, now trying with default branch") - tryBranch(targetRepo, "", pathElements[2:], - string(GiteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", - ) - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - return - } - - } else if bytes.HasSuffix(trimmedHost, MainDomainSuffix) { - // Serve pages from subdomains of MainDomainSuffix - log.Debug().Msg("main domain suffix") - - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") - targetOwner = string(bytes.TrimSuffix(trimmedHost, MainDomainSuffix)) - targetRepo = pathElements[0] - targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") - - if targetOwner == "www" { - // www.codeberg.page redirects to codeberg.page - ctx.Redirect("https://"+string(MainDomainSuffix[1:])+string(ctx.Path()), fasthttp.StatusPermanentRedirect) - return - } - - // Check if the first directory is a repo with the second directory as a branch - // example.codeberg.page/myrepo/@main/index.html - if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { - if targetRepo == "pages" { - // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... - ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) - return - } - - log.Debug().Msg("main domain preparations, now trying with specified repo & branch") - if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], - "/"+pathElements[0]+"/%p", - ) { - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - } - return - } - - // Check if the first directory is a branch for the "pages" repo - // example.codeberg.page/@main/index.html - if strings.HasPrefix(pathElements[0], "@") { - log.Debug().Msg("main domain preparations, now trying with specified branch") - if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - } - return - } - - // Check if the first directory is a repo with a "pages" branch - // example.codeberg.page/myrepo/index.html - // example.codeberg.page/pages/... is not allowed here. - log.Debug().Msg("main domain preparations, now trying with specified repo") - if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - return - } - - // Try to use the "pages" repo on its default branch - // example.codeberg.page/index.html - log.Debug().Msg("main domain preparations, now trying with default repo/branch") - if tryBranch("pages", "", pathElements, "") { - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - return - } - - // Couldn't find a valid repo/branch - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return - } else { - trimmedHostStr := string(trimmedHost) - - // Serve pages from external domains - targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr) - if targetOwner == "" { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return - } - - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") - canonicalLink := "" - if strings.HasPrefix(pathElements[0], "@") { - targetBranch = pathElements[0][1:] - pathElements = pathElements[1:] - canonicalLink = "/%p" - } - - // Try to use the given repo on the given branch or the default branch - log.Debug().Msg("custom domain preparations, now trying with details from DNS") - if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr) - if !valid { - returnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) - return - } else if canonicalDomain != trimmedHostStr { - // only redirect if the target is also a codeberg page! - targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0]) - if targetOwner != "" { - ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) - return - } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return - } - } - - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() - return - } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return - } - } -} - -// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced -// with the provided status code. -func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { - ctx.Response.SetStatusCode(code) - ctx.Response.Header.SetContentType("text/html; charset=utf-8") - message := fasthttp.StatusMessage(code) - if code == fasthttp.StatusMisdirectedRequest { - message += " - domain not specified in .domains file" - } - if code == fasthttp.StatusFailedDependency { - message += " - target repo/branch doesn't exist or is private" - } - // TODO: use template engine? - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) -} - -// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var DefaultBranchCacheTimeout = 15 * time.Minute - -// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter -// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be -// picked up faster, while still allowing the content to be cached longer if nothing changes. -var BranchExistanceCacheTimeout = 5 * time.Minute - -// branchTimestampCache stores branch timestamps for faster cache checking -var branchTimestampCache = mcache.New() - -type branchTimestamp struct { - branch string - timestamp time.Time -} - -// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending -// on your available memory. -var FileCacheTimeout = 5 * time.Minute - -// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. -var FileCacheSizeLimit = 1024 * 1024 - -// fileResponseCache stores responses from the Gitea server -// TODO: make this an MRU cache with a size limit -var fileResponseCache = mcache.New() - -type fileResponse struct { - exists bool - mimeType string - body []byte -} - -// getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch -// (or nil if the branch doesn't exist) -func getBranchTimestamp(owner, repo, branch string) *branchTimestamp { - if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { - if result == nil { - return nil - } - return result.(*branchTimestamp) - } - result := &branchTimestamp{} - result.branch = branch - if branch == "" { - // Get default branch - var body = make([]byte, 0) - // TODO: use header for API key? - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+GiteaApiToken, 5*time.Second) - if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) - return nil - } - result.branch = fastjson.GetString(body, "default_branch") - } - - var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, string(GiteaRoot)+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+GiteaApiToken, 5*time.Second) - if err != nil || status != 200 { - return nil - } - - result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) - return result -} - -var upstreamClient = fasthttp.Client{ - ReadTimeout: 10 * time.Second, - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! -} - -// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath string, options *upstreamOptions) (final bool) { - log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() - - if options.ForbiddenMimeTypes == nil { - options.ForbiddenMimeTypes = map[string]struct{}{} - } - - // Check if the branch exists and when it was modified - if options.BranchTimestamp == (time.Time{}) { - branch := getBranchTimestamp(targetOwner, targetRepo, targetBranch) - - if branch == nil { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return true - } - targetBranch = branch.branch - options.BranchTimestamp = branch.timestamp - } - - if targetOwner == "" || targetRepo == "" || targetBranch == "" { - returnErrorPage(ctx, fasthttp.StatusBadRequest) - return true - } - - // Check if the browser has a cached version - if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { - if !ifModifiedSince.Before(options.BranchTimestamp) { - ctx.Response.SetStatusCode(fasthttp.StatusNotModified) - return true - } - } - log.Debug().Msg("preparations") - - // Make a GET request to the upstream URL - uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath - var req *fasthttp.Request - var res *fasthttp.Response - var cachedResponse fileResponse - var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { - cachedResponse = cachedValue.(fileResponse) - } else { - req = fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + uri + "?access_token=" + GiteaApiToken) - res = fasthttp.AcquireResponse() - res.SetBodyStream(&strings.Reader{}, -1) - err = upstreamClient.Do(req, res) - } - log.Debug().Msg("acquisition") - - // Handle errors - if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { - if options.TryIndexPages { - // copy the options struct & try if an index page exists - optionsForIndexPages := *options - optionsForIndexPages.TryIndexPages = false - optionsForIndexPages.AppendTrailingSlash = true - for _, indexPage := range IndexPages { - if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, &optionsForIndexPages) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - return true - } - } - // compatibility fix for GitHub Pages (/example → /example.html) - optionsForIndexPages.AppendTrailingSlash = false - optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", &optionsForIndexPages) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - return true - } - } - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - if res != nil { - // Update cache if the request is fresh - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - } - return false - } - if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { - fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - - // Append trailing slash if missing (for index files), and redirect to fix filenames in general - // options.AppendTrailingSlash is only true when looking for index pages - if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { - ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) - return true - } - if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { - ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) - return true - } - if options.RedirectIfExists != "" { - ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) - return true - } - log.Debug().Msg("error handling") - - // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(targetPath)) - mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { - if options.DefaultMimeType != "" { - mimeType = options.DefaultMimeType - } else { - mimeType = "application/octet-stream" - } - } - ctx.Response.Header.SetContentType(mimeType) - - // Everything's okay so far - ctx.Response.SetStatusCode(fasthttp.StatusOK) - ctx.Response.Header.SetLastModified(options.BranchTimestamp) - - log.Debug().Msg("response preparations") - - // Write the response body to the original request - var cacheBodyWriter bytes.Buffer - if res != nil { - if res.Header.ContentLength() > FileCacheSizeLimit { - err = res.BodyWriteTo(ctx.Response.BodyWriter()) - } else { - // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? - err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) - } - } else { - _, err = ctx.Write(cachedResponse.body) - } - if err != nil { - fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - log.Debug().Msg("response") - - if res != nil && ctx.Err() == nil { - cachedResponse.exists = true - cachedResponse.mimeType = mimeType - cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) - } - - return true -} - -// upstreamOptions provides various options for the upstream request. -type upstreamOptions struct { - DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} - TryIndexPages bool - AppendTrailingSlash bool - RedirectIfExists string - BranchTimestamp time.Time -} diff --git a/404.html b/html/404.html similarity index 100% rename from 404.html rename to html/404.html diff --git a/html/html.go b/html/html.go new file mode 100644 index 0000000..d223e15 --- /dev/null +++ b/html/html.go @@ -0,0 +1,6 @@ +package html + +import _ "embed" + +//go:embed 404.html +var NotFoundPage []byte diff --git a/main.go b/main.go index 1a4cb65..501e0ab 100644 --- a/main.go +++ b/main.go @@ -26,31 +26,28 @@ import ( "os" "time" - _ "embed" - "github.com/valyala/fasthttp" + + pages_server "codeberg.org/codeberg/pages/server" ) // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". -var MainDomainSuffix = []byte("." + envOr("PAGES_DOMAIN", "codeberg.page")) +var MainDomainSuffix = []byte("." + pages_server.EnvOr("PAGES_DOMAIN", "codeberg.page")) // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. -var GiteaRoot = []byte(envOr("GITEA_ROOT", "https://codeberg.org")) +var GiteaRoot = []byte(pages_server.EnvOr("GITEA_ROOT", "https://codeberg.org")) -var GiteaApiToken = envOr("GITEA_API_TOKEN", "") - -//go:embed 404.html -var NotFoundPage []byte +var GiteaApiToken = pages_server.EnvOr("GITEA_API_TOKEN", "") // RawDomain specifies the domain from which raw repository content shall be served in the following format: // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} // (set to []byte(nil) to disable raw content hosting) -var RawDomain = []byte(envOr("RAW_DOMAIN", "raw.codeberg.org")) +var RawDomain = []byte(pages_server.EnvOr("RAW_DOMAIN", "raw.codeberg.org")) // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). -var RawInfoPage = envOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") +var RawInfoPage = pages_server.EnvOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. var AllowedCorsDomains = [][]byte{ @@ -64,11 +61,6 @@ var BlacklistedPaths = [][]byte{ []byte("/.well-known/acme-challenge/"), } -// IndexPages lists pages that may be considered as index pages for directories. -var IndexPages = []string{ - "index.html", -} - // main sets up and starts the web server. func main() { // TODO: CLI Library @@ -77,15 +69,15 @@ func main() { println("--remove-certificate requires at least one domain as an argument") os.Exit(1) } - if keyDatabaseErr != nil { - panic(keyDatabaseErr) + if pages_server.KeyDatabaseErr != nil { + panic(pages_server.KeyDatabaseErr) } for _, domain := range os.Args[2:] { - if err := keyDatabase.Delete([]byte(domain)); err != nil { + if err := pages_server.KeyDatabase.Delete([]byte(domain)); err != nil { panic(err) } } - if err := keyDatabase.Sync(); err != nil { + if err := pages_server.KeyDatabase.Sync(); err != nil { panic(err) } os.Exit(0) @@ -98,10 +90,13 @@ func main() { GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) // Use HOST and PORT environment variables to determine listening address - address := fmt.Sprintf("%s:%s", envOr("HOST", "[::]"), envOr("PORT", "443")) + address := fmt.Sprintf("%s:%s", pages_server.EnvOr("HOST", "[::]"), pages_server.EnvOr("PORT", "443")) log.Printf("Listening on https://%s", address) - // Enable compression by wrapping the handler() method with the compression function provided by FastHTTP + // Create handler based on settings + handler := pages_server.Handler(MainDomainSuffix, RawDomain, GiteaRoot, RawInfoPage, GiteaApiToken, BlacklistedPaths, AllowedCorsDomains) + + // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) server := &fasthttp.Server{ @@ -120,15 +115,15 @@ func main() { if err != nil { log.Fatalf("Couldn't create listener: %s", err) } - listener = tls.NewListener(listener, tlsConfig) + listener = tls.NewListener(listener, pages_server.TlsConfig(MainDomainSuffix, string(GiteaRoot), GiteaApiToken)) - setupCertificates() + pages_server.SetupCertificates(MainDomainSuffix) if os.Getenv("ENABLE_HTTP_SERVER") == "true" { go (func() { challengePath := []byte("/.well-known/acme-challenge/") err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(string(TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + challenge, ok := pages_server.ChallengeCache.Get(string(pages_server.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) if !ok || challenge == nil { ctx.SetStatusCode(http.StatusNotFound) ctx.SetBodyString("no challenge for this token") @@ -150,12 +145,3 @@ func main() { log.Fatalf("Couldn't start server: %s", err) } } - -// envOr reads an environment variable and returns a default value if it's empty. -// TODO: to helpers.go or use CLI framework -func envOr(env string, or string) string { - if v := os.Getenv(env); v != "" { - return v - } - return or -} diff --git a/certificates.go b/server/certificates.go similarity index 74% rename from certificates.go rename to server/certificates.go index 73b4793..c330439 100644 --- a/certificates.go +++ b/server/certificates.go @@ -1,4 +1,4 @@ -package main +package server import ( "bytes" @@ -37,102 +37,104 @@ import ( "github.com/go-acme/lego/v4/registration" ) -// tlsConfig contains the configuration for generating, serving and cleaning up Let's Encrypt certificates. -var tlsConfig = &tls.Config{ - // check DNS name & get certificate from Let's Encrypt - GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - sni := strings.ToLower(strings.TrimSpace(info.ServerName)) - sniBytes := []byte(sni) - if len(sni) < 1 { - return nil, errors.New("missing sni") - } +// TlsConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. +func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken string) *tls.Config { + return &tls.Config{ + // check DNS name & get certificate from Let's Encrypt + GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + sni := strings.ToLower(strings.TrimSpace(info.ServerName)) + sniBytes := []byte(sni) + if len(sni) < 1 { + return nil, errors.New("missing sni") + } - if info.SupportedProtos != nil { - for _, proto := range info.SupportedProtos { - if proto == tlsalpn01.ACMETLS1Protocol { - challenge, ok := challengeCache.Get(sni) - if !ok { - return nil, errors.New("no challenge for this domain") + if info.SupportedProtos != nil { + for _, proto := range info.SupportedProtos { + if proto == tlsalpn01.ACMETLS1Protocol { + challenge, ok := ChallengeCache.Get(sni) + if !ok { + return nil, errors.New("no challenge for this domain") + } + cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) + if err != nil { + return nil, err + } + return cert, nil } - cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) - if err != nil { - return nil, err - } - return cert, nil } } - } - targetOwner := "" - if bytes.HasSuffix(sniBytes, MainDomainSuffix) || bytes.Equal(sniBytes, MainDomainSuffix[1:]) { - // deliver default certificate for the main domain (*.codeberg.page) - sniBytes = MainDomainSuffix - sni = string(sniBytes) - } else { - var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni) - if targetOwner == "" { - // DNS not set up, return main certificate to redirect to the docs - sniBytes = MainDomainSuffix + targetOwner := "" + if bytes.HasSuffix(sniBytes, mainDomainSuffix) || bytes.Equal(sniBytes, mainDomainSuffix[1:]) { + // deliver default certificate for the main domain (*.codeberg.page) + sniBytes = mainDomainSuffix sni = string(sniBytes) } else { - _, _ = targetRepo, targetBranch - _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni) - if !valid { - sniBytes = MainDomainSuffix + var targetRepo, targetBranch string + targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix)) + if targetOwner == "" { + // DNS not set up, return main certificate to redirect to the docs + sniBytes = mainDomainSuffix sni = string(sniBytes) + } else { + _, _ = targetRepo, targetBranch + _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken) + if !valid { + sniBytes = mainDomainSuffix + sni = string(sniBytes) + } } } - } - if tlsCertificate, ok := keyCache.Get(sni); ok { - // we can use an existing certificate object - return tlsCertificate.(*tls.Certificate), nil - } - - var tlsCertificate tls.Certificate - var err error - var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes); !ok { - // request a new certificate - if bytes.Equal(sniBytes, MainDomainSuffix) { - return nil, errors.New("won't request certificate for main domain, something really bad has happened") + if tlsCertificate, ok := keyCache.Get(sni); ok { + // we can use an existing certificate object + return tlsCertificate.(*tls.Certificate), nil } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner) + var tlsCertificate tls.Certificate + var err error + var ok bool + if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix); !ok { + // request a new certificate + if bytes.Equal(sniBytes, mainDomainSuffix) { + return nil, errors.New("won't request certificate for main domain, something really bad has happened") + } + + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, mainDomainSuffix) + if err != nil { + return nil, err + } + } + + err = keyCache.Set(sni, &tlsCertificate, 15*time.Minute) if err != nil { - return nil, err + panic(err) } - } + return &tlsCertificate, nil + }, + PreferServerCipherSuites: true, + NextProtos: []string{ + "http/1.1", + tlsalpn01.ACMETLS1Protocol, + }, - err = keyCache.Set(sni, &tlsCertificate, 15*time.Minute) - if err != nil { - panic(err) - } - return &tlsCertificate, nil - }, - PreferServerCipherSuites: true, - NextProtos: []string{ - "http/1.1", - tlsalpn01.ACMETLS1Protocol, - }, - - // generated 2021-07-13, Mozilla Guideline v5.6, Go 1.14.4, intermediate configuration - // https://ssl-config.mozilla.org/#server=go&version=1.14.4&config=intermediate&guideline=5.6 - MinVersion: tls.VersionTLS12, - CipherSuites: []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - }, + // generated 2021-07-13, Mozilla Guideline v5.6, Go 1.14.4, intermediate configuration + // https://ssl-config.mozilla.org/#server=go&version=1.14.4&config=intermediate&guideline=5.6 + MinVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + }, + } } // TODO: clean up & move to init var keyCache = mcache.New() -var keyDatabase, keyDatabaseErr = pogreb.Open("key-database.pogreb", &pogreb.Options{ +var KeyDatabase, KeyDatabaseErr = pogreb.Open("key-database.pogreb", &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, BackgroundCompactionInterval: 6 * time.Hour, FileSystem: fs.OSMMap, @@ -181,17 +183,17 @@ var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) // rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) var acmeClientRequestLimit = equalizer.NewTokenBucket(5, 1*time.Second) -var challengeCache = mcache.New() +var ChallengeCache = mcache.New() type AcmeTLSChallengeProvider struct{} var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { - return challengeCache.Set(domain, keyAuth, 1*time.Hour) + return ChallengeCache.Set(domain, keyAuth, 1*time.Hour) } func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { - challengeCache.Remove(domain) + ChallengeCache.Remove(domain) return nil } @@ -200,17 +202,17 @@ type AcmeHTTPChallengeProvider struct{} var _ challenge.Provider = AcmeHTTPChallengeProvider{} func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { - return challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) + return ChallengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) } func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { - challengeCache.Remove(domain + "/" + token) + ChallengeCache.Remove(domain + "/" + token) return nil } -func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix []byte) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} - if !PogrebGet(keyDatabase, sni, res) { + if !PogrebGet(KeyDatabase, sni, res) { return tls.Certificate{}, false } @@ -220,7 +222,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { } // TODO: document & put into own function - if !bytes.Equal(sni, MainDomainSuffix) { + if !bytes.Equal(sni, mainDomainSuffix) { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { panic(err) @@ -238,7 +240,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { } go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "") + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", mainDomainSuffix) if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) } @@ -251,7 +253,7 @@ func retrieveCertFromDB(sni []byte) (tls.Certificate, bool) { var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, mainDomainSuffix []byte) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -264,7 +266,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re time.Sleep(100 * time.Millisecond) _, working = obtainLocks.Load(name) } - cert, ok := retrieveCertFromDB([]byte(name)) + cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix) if !ok { return tls.Certificate{}, errors.New("certificate failed in synchronous request") } @@ -273,7 +275,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re defer obtainLocks.Delete(name) if acmeClient == nil { - return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!"), nil + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", string(mainDomainSuffix)), nil } // request actual cert @@ -315,15 +317,15 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) - PogrebPut(keyDatabase, []byte(name), renew) + PogrebPut(KeyDatabase, []byte(name), renew) return tlsCertificate, nil } } - return mockCert(domains[0], err.Error()), err + return mockCert(domains[0], err.Error(), string(mainDomainSuffix)), err } log.Printf("Obtained certificate for %v", domains) - PogrebPut(keyDatabase, []byte(name), res) + PogrebPut(KeyDatabase, []byte(name), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -331,7 +333,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return tlsCertificate, nil } -func mockCert(domain string, msg string) tls.Certificate { +func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) if err != nil { panic(err) @@ -385,10 +387,10 @@ func mockCert(domain string, msg string) tls.Certificate { Domain: domain, } databaseName := domain - if domain == "*"+string(MainDomainSuffix) || domain == string(MainDomainSuffix[1:]) { - databaseName = string(MainDomainSuffix) + if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { + databaseName = mainDomainSuffix } - PogrebPut(keyDatabase, []byte(databaseName), res) + PogrebPut(KeyDatabase, []byte(databaseName), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -397,9 +399,9 @@ func mockCert(domain string, msg string) tls.Certificate { return tlsCertificate } -func setupCertificates() { - if keyDatabaseErr != nil { - panic(keyDatabaseErr) +func SetupCertificates(mainDomainSuffix []byte) { + if KeyDatabaseErr != nil { + panic(KeyDatabaseErr) } if os.Getenv("ACME_ACCEPT_TERMS") != "true" || (os.Getenv("DNS_PROVIDER") == "" && os.Getenv("ACME_API") != "https://acme.mock.directory") { @@ -407,7 +409,7 @@ func setupCertificates() { } // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits - mainCertBytes, err := keyDatabase.Get(MainDomainSuffix) + mainCertBytes, err := KeyDatabase.Get(mainDomainSuffix) if err != nil { // key database is not working panic(err) @@ -423,7 +425,7 @@ func setupCertificates() { panic(err) } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") + myAcmeConfig.CADirURL = EnvOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 _, err := lego.NewClient(myAcmeConfig) if err != nil { @@ -435,12 +437,12 @@ func setupCertificates() { panic(err) } myAcmeAccount = AcmeAccount{ - Email: envOr("ACME_EMAIL", "noreply@example.email"), + Email: EnvOr("ACME_EMAIL", "noreply@example.email"), Key: privateKey, KeyPEM: string(certcrypto.PEMEncode(privateKey)), } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = envOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") + myAcmeConfig.CADirURL = EnvOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 tempClient, err := lego.NewClient(myAcmeConfig) if err != nil { @@ -523,7 +525,7 @@ func setupCertificates() { } if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, nil, "") + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", mainDomainSuffix) if err != nil { log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } @@ -531,7 +533,7 @@ func setupCertificates() { go (func() { for { - err := keyDatabase.Sync() + err := KeyDatabase.Sync() if err != nil { log.Printf("[ERROR] Syncing key database failed: %s", err) } @@ -544,10 +546,10 @@ func setupCertificates() { // clean up expired certs now := time.Now() expiredCertCount := 0 - keyDatabaseIterator := keyDatabase.Items() + keyDatabaseIterator := KeyDatabase.Items() key, resBytes, err := keyDatabaseIterator.Next() for err == nil { - if !bytes.Equal(key, MainDomainSuffix) { + if !bytes.Equal(key, mainDomainSuffix) { resGob := bytes.NewBuffer(resBytes) resDec := gob.NewDecoder(resGob) res := &certificate.Resource{} @@ -558,7 +560,7 @@ func setupCertificates() { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := keyDatabase.Delete(key) + err := KeyDatabase.Delete(key) if err != nil { log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) } else { @@ -571,7 +573,7 @@ func setupCertificates() { log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) // compact the database - result, err := keyDatabase.Compact() + result, err := KeyDatabase.Compact() if err != nil { log.Printf("[ERROR] Compacting key database failed: %s", err) } else { @@ -580,7 +582,7 @@ func setupCertificates() { // update main cert res := &certificate.Resource{} - if !PogrebGet(keyDatabase, MainDomainSuffix, res) { + if !PogrebGet(KeyDatabase, mainDomainSuffix, res) { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) @@ -588,7 +590,7 @@ func setupCertificates() { // renew main certificate 30 days before it expires if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(MainDomainSuffix), string(MainDomainSuffix[1:])}, res, "") + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", mainDomainSuffix) if err != nil { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) } diff --git a/domains.go b/server/domains.go similarity index 83% rename from domains.go rename to server/domains.go index 0a5abc1..5673960 100644 --- a/domains.go +++ b/server/domains.go @@ -1,4 +1,4 @@ -package main +package server import ( "github.com/OrlovEvgeny/go-mcache" @@ -16,7 +16,7 @@ var dnsLookupCache = mcache.New() // getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch string) { +func getTargetFromDNS(domain, mainDomainSuffix string) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error @@ -25,14 +25,14 @@ func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch stri } else { cname, err = net.LookupCNAME(domain) cname = strings.TrimSuffix(cname, ".") - if err != nil || !strings.HasSuffix(cname, string(MainDomainSuffix)) { + if err != nil || !strings.HasSuffix(cname, mainDomainSuffix) { cname = "" // TODO: check if the A record matches! names, err := net.LookupTXT(domain) if err == nil { for _, name := range names { name = strings.TrimSuffix(name, ".") - if strings.HasSuffix(name, string(MainDomainSuffix)) { + if strings.HasSuffix(name, mainDomainSuffix) { cname = name break } @@ -44,7 +44,7 @@ func getTargetFromDNS(domain string) (targetOwner, targetRepo, targetBranch stri if cname == "" { return } - cnameParts := strings.Split(strings.TrimSuffix(cname, string(MainDomainSuffix)), ".") + cnameParts := strings.Split(strings.TrimSuffix(cname, mainDomainSuffix), ".") targetOwner = cnameParts[len(cnameParts)-1] if len(cnameParts) > 1 { targetRepo = cnameParts[len(cnameParts)-2] @@ -69,7 +69,7 @@ var CanonicalDomainCacheTimeout = 15 * time.Minute var canonicalDomainCache = mcache.New() // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain string) (canonicalDomain string, valid bool) { +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string) (canonicalDomain string, valid bool) { domains := []string{} if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) @@ -81,7 +81,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain st } } else { req := fasthttp.AcquireRequest() - req.SetRequestURI(string(GiteaRoot) + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + GiteaApiToken) + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) res := fasthttp.AcquireResponse() err := upstreamClient.Do(req, res) @@ -99,7 +99,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain st } } } - domains = append(domains, targetOwner+string(MainDomainSuffix)) + domains = append(domains, targetOwner+mainDomainSuffix) if domains[len(domains)-1] == actualDomain { valid = true } diff --git a/server/handler.go b/server/handler.go new file mode 100644 index 0000000..f24227b --- /dev/null +++ b/server/handler.go @@ -0,0 +1,553 @@ +package server + +import ( + "bytes" + "fmt" + "io" + "mime" + "path" + "strconv" + "strings" + "time" + + "github.com/OrlovEvgeny/go-mcache" + "github.com/rs/zerolog/log" + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" + + "codeberg.org/codeberg/pages/html" +) + +// Handler handles a single HTTP request to the web server. +func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte) func(ctx *fasthttp.RequestCtx) { + return func(ctx *fasthttp.RequestCtx) { + log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() + + ctx.Response.Header.Set("Server", "Codeberg Pages") + + // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin + ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") + + // Enable browser caching for up to 10 minutes + ctx.Response.Header.Set("Cache-Control", "public, max-age=600") + + trimmedHost := TrimHostPort(ctx.Request.Host()) + + // Add HSTS for RawDomain and MainDomainSuffix + if hsts := GetHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { + ctx.Response.Header.Set("Strict-Transport-Security", hsts) + } + + // Block all methods not required for static pages + if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) + return + } + + // Block blacklisted paths (like ACME challenges) + for _, blacklistedPath := range blacklistedPaths { + if bytes.HasPrefix(ctx.Path(), blacklistedPath) { + returnErrorPage(ctx, fasthttp.StatusForbidden) + return + } + } + + // Allow CORS for specified domains + if ctx.IsOptions() { + allowCors := false + for _, allowedCorsDomain := range allowedCorsDomains { + if bytes.Equal(trimmedHost, allowedCorsDomain) { + allowCors = true + break + } + } + if allowCors { + ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") + } + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") + ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) + return + } + + // Prepare request information to Gitea + var targetOwner, targetRepo, targetBranch, targetPath string + var targetOptions = &upstreamOptions{ + ForbiddenMimeTypes: map[string]struct{}{}, + TryIndexPages: true, + } + + // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will + // also disallow search indexing and add a Link header to the canonical URL. + var tryBranch = func(repo string, branch string, path []string, canonicalLink string) bool { + if repo == "" { + return false + } + + // Check if the branch exists, otherwise treat it as a file path + branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch, string(giteaRoot), giteaApiToken) + if branchTimestampResult == nil { + // branch doesn't exist + return false + } + + // Branch exists, use it + targetRepo = repo + targetPath = strings.Trim(strings.Join(path, "/"), "/") + targetBranch = branchTimestampResult.branch + + targetOptions.BranchTimestamp = branchTimestampResult.timestamp + + if canonicalLink != "" { + // Hide from search machines & add canonical link + ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") + ctx.Response.Header.Set("Link", + strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ + "; rel=\"canonical\"", + ) + } + + return true + } + + // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. + var tryUpstream = func() { + // check if a canonical domain exists on a request on MainDomain + if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { + canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), string(giteaRoot), giteaApiToken) + if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { + canonicalPath := string(ctx.RequestURI()) + if targetRepo != "pages" { + canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + } + ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) + return + } + } + + // Try to request the file from the Gitea API + if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, string(giteaRoot), giteaApiToken, targetOptions) { + returnErrorPage(ctx, ctx.Response.StatusCode()) + } + } + + log.Debug().Msg("preparations") + + if rawDomain != nil && bytes.Equal(trimmedHost, rawDomain) { + // Serve raw content from RawDomain + log.Debug().Msg("raw domain") + + targetOptions.TryIndexPages = false + targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} + targetOptions.DefaultMimeType = "text/plain; charset=utf-8" + + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + if len(pathElements) < 2 { + // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required + ctx.Redirect(rawInfoPage, fasthttp.StatusTemporaryRedirect) + return + } + targetOwner = pathElements[0] + targetRepo = pathElements[1] + + // raw.codeberg.org/example/myrepo/@main/index.html + if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { + log.Debug().Msg("raw domain preparations, now trying with specified branch") + if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], + string(giteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + ) { + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + return + } + log.Debug().Msg("missing branch") + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } else { + log.Debug().Msg("raw domain preparations, now trying with default branch") + tryBranch(targetRepo, "", pathElements[2:], + string(giteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + ) + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + return + } + + } else if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { + // Serve pages from subdomains of MainDomainSuffix + log.Debug().Msg("main domain suffix") + + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + targetOwner = string(bytes.TrimSuffix(trimmedHost, mainDomainSuffix)) + targetRepo = pathElements[0] + targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") + + if targetOwner == "www" { + // www.codeberg.page redirects to codeberg.page + ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), fasthttp.StatusPermanentRedirect) + return + } + + // Check if the first directory is a repo with the second directory as a branch + // example.codeberg.page/myrepo/@main/index.html + if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { + if targetRepo == "pages" { + // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... + ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) + return + } + + log.Debug().Msg("main domain preparations, now trying with specified repo & branch") + if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], + "/"+pathElements[0]+"/%p", + ) { + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + } + return + } + + // Check if the first directory is a branch for the "pages" repo + // example.codeberg.page/@main/index.html + if strings.HasPrefix(pathElements[0], "@") { + log.Debug().Msg("main domain preparations, now trying with specified branch") + if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + } + return + } + + // Check if the first directory is a repo with a "pages" branch + // example.codeberg.page/myrepo/index.html + // example.codeberg.page/pages/... is not allowed here. + log.Debug().Msg("main domain preparations, now trying with specified repo") + if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + return + } + + // Try to use the "pages" repo on its default branch + // example.codeberg.page/index.html + log.Debug().Msg("main domain preparations, now trying with default repo/branch") + if tryBranch("pages", "", pathElements, "") { + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + return + } + + // Couldn't find a valid repo/branch + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } else { + trimmedHostStr := string(trimmedHost) + + // Serve pages from external domains + targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix)) + if targetOwner == "" { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } + + pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + canonicalLink := "" + if strings.HasPrefix(pathElements[0], "@") { + targetBranch = pathElements[0][1:] + pathElements = pathElements[1:] + canonicalLink = "/%p" + } + + // Try to use the given repo on the given branch or the default branch + log.Debug().Msg("custom domain preparations, now trying with details from DNS") + if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { + canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), string(giteaRoot), giteaApiToken) + if !valid { + returnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) + return + } else if canonicalDomain != trimmedHostStr { + // only redirect if the target is also a codeberg page! + targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) + if targetOwner != "" { + ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) + return + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } + } + + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream() + return + } else { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return + } + } + } +} + +// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced +// with the provided status code. +func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { + ctx.Response.SetStatusCode(code) + ctx.Response.Header.SetContentType("text/html; charset=utf-8") + message := fasthttp.StatusMessage(code) + if code == fasthttp.StatusMisdirectedRequest { + message += " - domain not specified in .domains file" + } + if code == fasthttp.StatusFailedDependency { + message += " - target repo/branch doesn't exist or is private" + } + // TODO: use template engine? + ctx.Response.SetBody(bytes.ReplaceAll(html.NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) +} + +// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. +var DefaultBranchCacheTimeout = 15 * time.Minute + +// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter +// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be +// picked up faster, while still allowing the content to be cached longer if nothing changes. +var BranchExistanceCacheTimeout = 5 * time.Minute + +// branchTimestampCache stores branch timestamps for faster cache checking +var branchTimestampCache = mcache.New() + +type branchTimestamp struct { + branch string + timestamp time.Time +} + +// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending +// on your available memory. +var FileCacheTimeout = 5 * time.Minute + +// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. +var FileCacheSizeLimit = 1024 * 1024 + +// fileResponseCache stores responses from the Gitea server +// TODO: make this an MRU cache with a size limit +var fileResponseCache = mcache.New() + +type fileResponse struct { + exists bool + mimeType string + body []byte +} + +// getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch +// (or nil if the branch doesn't exist) +func getBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string) *branchTimestamp { + if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { + if result == nil { + return nil + } + return result.(*branchTimestamp) + } + result := &branchTimestamp{} + result.branch = branch + if branch == "" { + // Get default branch + var body = make([]byte, 0) + // TODO: use header for API key? + status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) + if err != nil || status != 200 { + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) + return nil + } + result.branch = fastjson.GetString(body, "default_branch") + } + + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) + if err != nil || status != 200 { + return nil + } + + result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) + return result +} + +var upstreamClient = fasthttp.Client{ + ReadTimeout: 10 * time.Second, + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! +} + +// upstreamIndexPages lists pages that may be considered as index pages for directories. +var upstreamIndexPages = []string{ + "index.html", +} + +// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. +func upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *upstreamOptions) (final bool) { + log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() + + if options.ForbiddenMimeTypes == nil { + options.ForbiddenMimeTypes = map[string]struct{}{} + } + + // Check if the branch exists and when it was modified + if options.BranchTimestamp == (time.Time{}) { + branch := getBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken) + + if branch == nil { + returnErrorPage(ctx, fasthttp.StatusFailedDependency) + return true + } + targetBranch = branch.branch + options.BranchTimestamp = branch.timestamp + } + + if targetOwner == "" || targetRepo == "" || targetBranch == "" { + returnErrorPage(ctx, fasthttp.StatusBadRequest) + return true + } + + // Check if the browser has a cached version + if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { + if !ifModifiedSince.Before(options.BranchTimestamp) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + return true + } + } + log.Debug().Msg("preparations") + + // Make a GET request to the upstream URL + uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath + var req *fasthttp.Request + var res *fasthttp.Response + var cachedResponse fileResponse + var err error + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { + cachedResponse = cachedValue.(fileResponse) + } else { + req = fasthttp.AcquireRequest() + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaApiToken) + res = fasthttp.AcquireResponse() + res.SetBodyStream(&strings.Reader{}, -1) + err = upstreamClient.Do(req, res) + } + log.Debug().Msg("acquisition") + + // Handle errors + if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { + if options.TryIndexPages { + // copy the options struct & try if an index page exists + optionsForIndexPages := *options + optionsForIndexPages.TryIndexPages = false + optionsForIndexPages.AppendTrailingSlash = true + for _, indexPage := range upstreamIndexPages { + if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + return true + } + } + // compatibility fix for GitHub Pages (/example → /example.html) + optionsForIndexPages.AppendTrailingSlash = false + optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" + if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + return true + } + } + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + if res != nil { + // Update cache if the request is fresh + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + } + return false + } + if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { + fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + + // Append trailing slash if missing (for index files), and redirect to fix filenames in general + // options.AppendTrailingSlash is only true when looking for index pages + if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { + ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) + return true + } + if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { + ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) + return true + } + if options.RedirectIfExists != "" { + ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) + return true + } + log.Debug().Msg("error handling") + + // Set the MIME type + mimeType := mime.TypeByExtension(path.Ext(targetPath)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { + if options.DefaultMimeType != "" { + mimeType = options.DefaultMimeType + } else { + mimeType = "application/octet-stream" + } + } + ctx.Response.Header.SetContentType(mimeType) + + // Everything's okay so far + ctx.Response.SetStatusCode(fasthttp.StatusOK) + ctx.Response.Header.SetLastModified(options.BranchTimestamp) + + log.Debug().Msg("response preparations") + + // Write the response body to the original request + var cacheBodyWriter bytes.Buffer + if res != nil { + if res.Header.ContentLength() > FileCacheSizeLimit { + err = res.BodyWriteTo(ctx.Response.BodyWriter()) + } else { + // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? + err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + } + } else { + _, err = ctx.Write(cachedResponse.body) + } + if err != nil { + fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) + returnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + log.Debug().Msg("response") + + if res != nil && ctx.Err() == nil { + cachedResponse.exists = true + cachedResponse.mimeType = mimeType + cachedResponse.body = cacheBodyWriter.Bytes() + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) + } + + return true +} + +// upstreamOptions provides various options for the upstream request. +type upstreamOptions struct { + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool + AppendTrailingSlash bool + RedirectIfExists string + BranchTimestamp time.Time +} diff --git a/handler_test.go b/server/handler_test.go similarity index 80% rename from handler_test.go rename to server/handler_test.go index 70b655e..78357e7 100644 --- a/handler_test.go +++ b/server/handler_test.go @@ -1,4 +1,4 @@ -package main +package server import ( "fmt" @@ -8,6 +8,16 @@ import ( ) func TestHandlerPerformance(t *testing.T) { + testHandler := Handler( + []byte("codeberg.page"), + []byte("raw.codeberg.org"), + []byte("https://codeberg.org"), + "https://docs.codeberg.org/pages/raw-content/", + "", + [][]byte{[]byte("/.well-known/acme-challenge/")}, + [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, + ) + ctx := &fasthttp.RequestCtx{ Request: *fasthttp.AcquireRequest(), Response: *fasthttp.AcquireResponse(), @@ -15,7 +25,7 @@ func TestHandlerPerformance(t *testing.T) { ctx.Request.SetRequestURI("http://mondstern.codeberg.page/") fmt.Printf("Start: %v\n", time.Now()) start := time.Now() - handler(ctx) + testHandler(ctx) end := time.Now() fmt.Printf("Done: %v\n", time.Now()) if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { @@ -28,7 +38,7 @@ func TestHandlerPerformance(t *testing.T) { ctx.Response.ResetBody() fmt.Printf("Start: %v\n", time.Now()) start = time.Now() - handler(ctx) + testHandler(ctx) end = time.Now() fmt.Printf("Done: %v\n", time.Now()) if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { @@ -42,7 +52,7 @@ func TestHandlerPerformance(t *testing.T) { ctx.Request.SetRequestURI("http://example.momar.xyz/") fmt.Printf("Start: %v\n", time.Now()) start = time.Now() - handler(ctx) + testHandler(ctx) end = time.Now() fmt.Printf("Done: %v\n", time.Now()) if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 1 { diff --git a/helpers.go b/server/helpers.go similarity index 71% rename from helpers.go rename to server/helpers.go index 46a1492..354f15e 100644 --- a/helpers.go +++ b/server/helpers.go @@ -1,15 +1,17 @@ -package main +package server import ( "bytes" "encoding/gob" + "os" + "github.com/akrylysov/pogreb" ) // GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty // string for custom domains. -func GetHSTSHeader(host []byte) string { - if bytes.HasSuffix(host, MainDomainSuffix) || bytes.Equal(host, RawDomain) { +func GetHSTSHeader(host, mainDomainSuffix, rawDomain []byte) string { + if bytes.HasSuffix(host, mainDomainSuffix) || bytes.Equal(host, rawDomain) { return "max-age=63072000; includeSubdomains; preload" } else { return "" @@ -54,3 +56,12 @@ func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { } return true } + +// EnvOr reads an environment variable and returns a default value if it's empty. +// TODO: to helpers.go or use CLI framework +func EnvOr(env string, or string) string { + if v := os.Getenv(env); v != "" { + return v + } + return or +} From ac93a5661c1fdc015c7bbabf3dd2c0c6789bb1f3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 02:12:51 +0100 Subject: [PATCH 063/320] start using urfave/cli --- README.md | 18 +++++++ cmd/certs.go | 40 ++++++++++++++ cmd/flags.go | 37 +++++++++++++ cmd/main.go | 98 +++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 5 ++ main.go | 149 ++++++--------------------------------------------- 7 files changed, 216 insertions(+), 132 deletions(-) create mode 100644 cmd/certs.go create mode 100644 cmd/flags.go create mode 100644 cmd/main.go diff --git a/README.md b/README.md index 35c230e..044379e 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,21 @@ - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. + + +// Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. +// +// Mapping custom domains is not static anymore, but can be done with DNS: +// +// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. The +// first line will be the canonical domain/URL; all other occurrences will be redirected to it. +// +// 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to +// "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): +// www.example.org. IN CNAME main.pages.example.codeberg.page. +// +// 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record +// for "example.org" (if your provider allows ALIAS or similar records): +// example.org IN ALIAS codeberg.page. +// +// Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. diff --git a/cmd/certs.go b/cmd/certs.go new file mode 100644 index 0000000..4676520 --- /dev/null +++ b/cmd/certs.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" + + pages_server "codeberg.org/codeberg/pages/server" +) + +var Certs = &cli.Command{ + Name: "certs", + Usage: "manage certs manually", + Action: certs, +} + +func certs(ctx *cli.Context) error { + if ctx.Args().Len() >= 1 && ctx.Args().First() == "--remove-certificate" { + if ctx.Args().Len() == 1 { + println("--remove-certificate requires at least one domain as an argument") + os.Exit(1) + } + + domains := ctx.Args().Slice()[2:] + + if pages_server.KeyDatabaseErr != nil { + panic(pages_server.KeyDatabaseErr) + } + for _, domain := range domains { + if err := pages_server.KeyDatabase.Delete([]byte(domain)); err != nil { + panic(err) + } + } + if err := pages_server.KeyDatabase.Sync(); err != nil { + panic(err) + } + os.Exit(0) + } + return nil +} diff --git a/cmd/flags.go b/cmd/flags.go new file mode 100644 index 0000000..258307f --- /dev/null +++ b/cmd/flags.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "codeberg.org/codeberg/pages/server" + "github.com/urfave/cli/v2" +) + +// GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. +var GiteaRoot = []byte(server.EnvOr("GITEA_ROOT", "https://codeberg.org")) + +var GiteaApiToken = server.EnvOr("GITEA_API_TOKEN", "") + +// RawDomain specifies the domain from which raw repository content shall be served in the following format: +// https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} +// (set to []byte(nil) to disable raw content hosting) +var RawDomain = []byte(server.EnvOr("RAW_DOMAIN", "raw.codeberg.org")) + +// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). +var RawInfoPage = server.EnvOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") + +var ServeFlags = []cli.Flag{ + // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static + // pages, or used for comparison in CNAME lookups. Static pages can be accessed through + // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". + // var MainDomainSuffix = []byte("." + server.EnvOr("PAGES_DOMAIN", "codeberg.page")) + &cli.StringFlag{ + Name: "main-domain-suffix", + Aliases: nil, + Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", + EnvVars: []string{"PAGES_DOMAIN"}, + FilePath: "", + Required: false, + Hidden: false, + TakesFile: false, + Value: "codeberg.page", + }, +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..66f9e7b --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,98 @@ +package cmd + +import ( + "bytes" + "crypto/tls" + "fmt" + "log" + "net" + "net/http" + "os" + "time" + + "github.com/urfave/cli/v2" + "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/server" +) + +// AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. +var AllowedCorsDomains = [][]byte{ + RawDomain, + []byte("fonts.codeberg.org"), + []byte("design.codeberg.org"), +} + +// BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. +var BlacklistedPaths = [][]byte{ + []byte("/.well-known/acme-challenge/"), +} + +// Serve sets up and starts the web server. +func Serve(ctx *cli.Context) error { + mainDomainSuffix := []byte(ctx.String("main-domain-suffix")) + // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash + if !bytes.HasPrefix(mainDomainSuffix, []byte{'.'}) { + mainDomainSuffix = append([]byte{'.'}, mainDomainSuffix...) + } + + GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) + + // Use HOST and PORT environment variables to determine listening address + address := fmt.Sprintf("%s:%s", server.EnvOr("HOST", "[::]"), server.EnvOr("PORT", "443")) + log.Printf("Listening on https://%s", address) + + // Create handler based on settings + handler := server.Handler(mainDomainSuffix, RawDomain, GiteaRoot, RawInfoPage, GiteaApiToken, BlacklistedPaths, AllowedCorsDomains) + + // Enable compression by wrapping the handler with the compression function provided by FastHTTP + compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) + + fastServer := &fasthttp.Server{ + Handler: compressedHandler, + DisablePreParseMultipartForm: true, + MaxRequestBodySize: 0, + NoDefaultServerHeader: true, + NoDefaultDate: true, + ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge + Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! + MaxConnsPerIP: 100, + } + + // Setup listener and TLS + listener, err := net.Listen("tcp", address) + if err != nil { + log.Fatalf("Couldn't create listener: %s", err) + } + listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, string(GiteaRoot), GiteaApiToken)) + + server.SetupCertificates(mainDomainSuffix) + if os.Getenv("ENABLE_HTTP_SERVER") == "true" { + go (func() { + challengePath := []byte("/.well-known/acme-challenge/") + err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { + if bytes.HasPrefix(ctx.Path(), challengePath) { + challenge, ok := server.ChallengeCache.Get(string(server.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + if !ok || challenge == nil { + ctx.SetStatusCode(http.StatusNotFound) + ctx.SetBodyString("no challenge for this token") + } + ctx.SetBodyString(challenge.(string)) + } else { + ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) + } + }) + if err != nil { + log.Fatalf("Couldn't start HTTP fastServer: %s", err) + } + })() + } + + // Start the web fastServer + err = fastServer.Serve(listener) + if err != nil { + log.Fatalf("Couldn't start fastServer: %s", err) + } + + return nil +} diff --git a/go.mod b/go.mod index 55e4675..a2bd8ee 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-acme/lego/v4 v4.5.3 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.26.0 + github.com/urfave/cli/v2 v2.3.0 github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 ) diff --git a/go.sum b/go.sum index 65da291..d04f727 100644 --- a/go.sum +++ b/go.sum @@ -100,6 +100,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -427,6 +428,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= @@ -434,6 +436,7 @@ github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= @@ -483,7 +486,9 @@ github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggs github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= diff --git a/main.go b/main.go index 501e0ab..41aba22 100644 --- a/main.go +++ b/main.go @@ -1,147 +1,32 @@ -// Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. -// -// Mapping custom domains is not static anymore, but can be done with DNS: -// -// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. The -// first line will be the canonical domain/URL; all other occurrences will be redirected to it. -// -// 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to -// "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): -// www.example.org. IN CNAME main.pages.example.codeberg.page. -// -// 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record -// for "example.org" (if your provider allows ALIAS or similar records): -// example.org IN ALIAS codeberg.page. -// -// Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. package main import ( - "bytes" - "crypto/tls" "fmt" - "log" - "net" - "net/http" "os" - "time" - "github.com/valyala/fasthttp" + "github.com/urfave/cli/v2" - pages_server "codeberg.org/codeberg/pages/server" + "codeberg.org/codeberg/pages/cmd" ) -// MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static -// pages, or used for comparison in CNAME lookups. Static pages can be accessed through -// https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". -var MainDomainSuffix = []byte("." + pages_server.EnvOr("PAGES_DOMAIN", "codeberg.page")) +var ( + // can be changed with -X on compile + version = "dev" +) -// GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. -var GiteaRoot = []byte(pages_server.EnvOr("GITEA_ROOT", "https://codeberg.org")) - -var GiteaApiToken = pages_server.EnvOr("GITEA_API_TOKEN", "") - -// RawDomain specifies the domain from which raw repository content shall be served in the following format: -// https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} -// (set to []byte(nil) to disable raw content hosting) -var RawDomain = []byte(pages_server.EnvOr("RAW_DOMAIN", "raw.codeberg.org")) - -// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). -var RawInfoPage = pages_server.EnvOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") - -// AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. -var AllowedCorsDomains = [][]byte{ - RawDomain, - []byte("fonts.codeberg.org"), - []byte("design.codeberg.org"), -} - -// BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. -var BlacklistedPaths = [][]byte{ - []byte("/.well-known/acme-challenge/"), -} - -// main sets up and starts the web server. func main() { - // TODO: CLI Library - if len(os.Args) > 1 && os.Args[1] == "--remove-certificate" { - if len(os.Args) < 2 { - println("--remove-certificate requires at least one domain as an argument") - os.Exit(1) - } - if pages_server.KeyDatabaseErr != nil { - panic(pages_server.KeyDatabaseErr) - } - for _, domain := range os.Args[2:] { - if err := pages_server.KeyDatabase.Delete([]byte(domain)); err != nil { - panic(err) - } - } - if err := pages_server.KeyDatabase.Sync(); err != nil { - panic(err) - } - os.Exit(0) + app := cli.NewApp() + app.Name = "pages-server" + app.Version = version + app.Usage = "pages server" + app.Action = cmd.Serve + app.Flags = cmd.ServeFlags + app.Commands = []*cli.Command{ + cmd.Certs, } - // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash - if !bytes.HasPrefix(MainDomainSuffix, []byte{'.'}) { - MainDomainSuffix = append([]byte{'.'}, MainDomainSuffix...) - } - GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) - - // Use HOST and PORT environment variables to determine listening address - address := fmt.Sprintf("%s:%s", pages_server.EnvOr("HOST", "[::]"), pages_server.EnvOr("PORT", "443")) - log.Printf("Listening on https://%s", address) - - // Create handler based on settings - handler := pages_server.Handler(MainDomainSuffix, RawDomain, GiteaRoot, RawInfoPage, GiteaApiToken, BlacklistedPaths, AllowedCorsDomains) - - // Enable compression by wrapping the handler with the compression function provided by FastHTTP - compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) - - server := &fasthttp.Server{ - Handler: compressedHandler, - DisablePreParseMultipartForm: true, - MaxRequestBodySize: 0, - NoDefaultServerHeader: true, - NoDefaultDate: true, - ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge - Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! - MaxConnsPerIP: 100, - } - - // Setup listener and TLS - listener, err := net.Listen("tcp", address) - if err != nil { - log.Fatalf("Couldn't create listener: %s", err) - } - listener = tls.NewListener(listener, pages_server.TlsConfig(MainDomainSuffix, string(GiteaRoot), GiteaApiToken)) - - pages_server.SetupCertificates(MainDomainSuffix) - if os.Getenv("ENABLE_HTTP_SERVER") == "true" { - go (func() { - challengePath := []byte("/.well-known/acme-challenge/") - err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { - if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := pages_server.ChallengeCache.Get(string(pages_server.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) - if !ok || challenge == nil { - ctx.SetStatusCode(http.StatusNotFound) - ctx.SetBodyString("no challenge for this token") - } - ctx.SetBodyString(challenge.(string)) - } else { - ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) - } - }) - if err != nil { - log.Fatalf("Couldn't start HTTP server: %s", err) - } - })() - } - - // Start the web server - err = server.Serve(listener) - if err != nil { - log.Fatalf("Couldn't start server: %s", err) + if err := app.Run(os.Args); err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } From 35e08d2252dee948c1a1d528e95a2085736a2210 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 03:05:38 +0100 Subject: [PATCH 064/320] remove EnvOr use flags --- cmd/flags.go | 83 ++++++++++++++++++++++++++++++------------ cmd/main.go | 38 +++++++++++-------- server/certificates.go | 8 ++-- server/handler.go | 14 +++---- server/helpers.go | 11 ------ 5 files changed, 93 insertions(+), 61 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 258307f..209228e 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -1,37 +1,72 @@ package cmd import ( - "codeberg.org/codeberg/pages/server" "github.com/urfave/cli/v2" ) -// GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. -var GiteaRoot = []byte(server.EnvOr("GITEA_ROOT", "https://codeberg.org")) - -var GiteaApiToken = server.EnvOr("GITEA_API_TOKEN", "") - -// RawDomain specifies the domain from which raw repository content shall be served in the following format: -// https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} -// (set to []byte(nil) to disable raw content hosting) -var RawDomain = []byte(server.EnvOr("RAW_DOMAIN", "raw.codeberg.org")) - -// RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). -var RawInfoPage = server.EnvOr("REDIRECT_RAW_INFO", "https://docs.codeberg.org/pages/raw-content/") - var ServeFlags = []cli.Flag{ // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". - // var MainDomainSuffix = []byte("." + server.EnvOr("PAGES_DOMAIN", "codeberg.page")) &cli.StringFlag{ - Name: "main-domain-suffix", - Aliases: nil, - Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", - EnvVars: []string{"PAGES_DOMAIN"}, - FilePath: "", - Required: false, - Hidden: false, - TakesFile: false, - Value: "codeberg.page", + Name: "main-domain-suffix", + Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", + EnvVars: []string{"PAGES_DOMAIN"}, + Value: "codeberg.page", + }, + // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. + &cli.StringFlag{ + Name: "gitea-root", + Usage: "specifies the root URL of the Gitea instance, without a trailing slash.", + EnvVars: []string{"GITEA_ROOT"}, + Value: "https://codeberg.org", + }, + // GiteaApiToken specifies an api token for the Gitea instance + &cli.StringFlag{ + Name: "gitea-api-token", + Usage: "specifies an api token for the Gitea instance", + EnvVars: []string{"GITEA_API_TOKEN"}, + Value: "", + }, + // RawDomain specifies the domain from which raw repository content shall be served in the following format: + // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} + // (set to []byte(nil) to disable raw content hosting) + &cli.StringFlag{ + Name: "raw-domain", + Usage: "specifies the domain from which raw repository content shall be served, not set disable raw content hosting", + EnvVars: []string{"RAW_DOMAIN"}, + Value: "raw.codeberg.org", + }, + // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). + &cli.StringFlag{ + Name: "raw-info-page", + Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", + EnvVars: []string{"REDIRECT_RAW_INFO"}, + Value: "https://docs.codeberg.org/pages/raw-content/", + }, + + &cli.StringFlag{ + Name: "host", + Usage: "specifies host of listening address", + EnvVars: []string{"HOST"}, + Value: "[::]", + }, + &cli.StringFlag{ + Name: "port", + Usage: "specifies port of listening address", + EnvVars: []string{"PORT"}, + Value: "443", + }, + + // ACME_API + &cli.StringFlag{ + Name: "acme-api", + EnvVars: []string{"ACME_API"}, + Value: "https://acme-v02.api.letsencrypt.org/directory", + }, + &cli.StringFlag{ + Name: "acme-email", + EnvVars: []string{"ACME_EMAIL"}, + Value: "noreply@example.email", }, } diff --git a/cmd/main.go b/cmd/main.go index 66f9e7b..a46fdbc 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,12 +4,13 @@ import ( "bytes" "crypto/tls" "fmt" - "log" "net" "net/http" "os" + "strings" "time" + "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" "github.com/valyala/fasthttp" @@ -17,8 +18,8 @@ import ( ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. +// TODO: make it a flag var AllowedCorsDomains = [][]byte{ - RawDomain, []byte("fonts.codeberg.org"), []byte("design.codeberg.org"), } @@ -30,20 +31,26 @@ var BlacklistedPaths = [][]byte{ // Serve sets up and starts the web server. func Serve(ctx *cli.Context) error { + giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") + giteaAPIToken := ctx.String("gitea-api-token") + rawDomain := ctx.String("raw-domain") mainDomainSuffix := []byte(ctx.String("main-domain-suffix")) + rawInfoPage := ctx.String("raw-info-page") + listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) + acmeAPI := ctx.String("acme-api") + acmeMail := ctx.String("acme-email") + allowedCorsDomains := AllowedCorsDomains + if len(rawDomain) != 0 { + allowedCorsDomains = append(allowedCorsDomains, []byte(rawDomain)) + } + // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash if !bytes.HasPrefix(mainDomainSuffix, []byte{'.'}) { mainDomainSuffix = append([]byte{'.'}, mainDomainSuffix...) } - GiteaRoot = bytes.TrimSuffix(GiteaRoot, []byte{'/'}) - - // Use HOST and PORT environment variables to determine listening address - address := fmt.Sprintf("%s:%s", server.EnvOr("HOST", "[::]"), server.EnvOr("PORT", "443")) - log.Printf("Listening on https://%s", address) - // Create handler based on settings - handler := server.Handler(mainDomainSuffix, RawDomain, GiteaRoot, RawInfoPage, GiteaApiToken, BlacklistedPaths, AllowedCorsDomains) + handler := server.Handler(mainDomainSuffix, []byte(rawDomain), giteaRoot, rawInfoPage, giteaAPIToken, BlacklistedPaths, allowedCorsDomains) // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) @@ -60,13 +67,14 @@ func Serve(ctx *cli.Context) error { } // Setup listener and TLS - listener, err := net.Listen("tcp", address) + log.Info().Msgf("Listening on https://%s", listeningAddress) + listener, err := net.Listen("tcp", listeningAddress) if err != nil { - log.Fatalf("Couldn't create listener: %s", err) + return fmt.Errorf("couldn't create listener: %s", err) } - listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, string(GiteaRoot), GiteaApiToken)) + listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, giteaRoot, giteaAPIToken)) - server.SetupCertificates(mainDomainSuffix) + server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail) if os.Getenv("ENABLE_HTTP_SERVER") == "true" { go (func() { challengePath := []byte("/.well-known/acme-challenge/") @@ -83,7 +91,7 @@ func Serve(ctx *cli.Context) error { } }) if err != nil { - log.Fatalf("Couldn't start HTTP fastServer: %s", err) + log.Fatal().Err(err).Msg("Couldn't start HTTP fastServer") } })() } @@ -91,7 +99,7 @@ func Serve(ctx *cli.Context) error { // Start the web fastServer err = fastServer.Serve(listener) if err != nil { - log.Fatalf("Couldn't start fastServer: %s", err) + log.Fatal().Err(err).Msg("Couldn't start fastServer") } return nil diff --git a/server/certificates.go b/server/certificates.go index c330439..7b3e9d9 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -399,7 +399,7 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { return tlsCertificate } -func SetupCertificates(mainDomainSuffix []byte) { +func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { if KeyDatabaseErr != nil { panic(KeyDatabaseErr) } @@ -425,7 +425,7 @@ func SetupCertificates(mainDomainSuffix []byte) { panic(err) } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = EnvOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") + myAcmeConfig.CADirURL = acmeAPI myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 _, err := lego.NewClient(myAcmeConfig) if err != nil { @@ -437,12 +437,12 @@ func SetupCertificates(mainDomainSuffix []byte) { panic(err) } myAcmeAccount = AcmeAccount{ - Email: EnvOr("ACME_EMAIL", "noreply@example.email"), + Email: acmeMail, Key: privateKey, KeyPEM: string(certcrypto.PEMEncode(privateKey)), } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = EnvOr("ACME_API", "https://acme-v02.api.letsencrypt.org/directory") + myAcmeConfig.CADirURL = acmeAPI myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 tempClient, err := lego.NewClient(myAcmeConfig) if err != nil { diff --git a/server/handler.go b/server/handler.go index f24227b..ca2872f 100644 --- a/server/handler.go +++ b/server/handler.go @@ -19,7 +19,7 @@ import ( ) // Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte) func(ctx *fasthttp.RequestCtx) { +func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() @@ -86,7 +86,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp } // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch, string(giteaRoot), giteaApiToken) + branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken) if branchTimestampResult == nil { // branch doesn't exist return false @@ -115,7 +115,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp var tryUpstream = func() { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), string(giteaRoot), giteaApiToken) + canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -127,7 +127,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp } // Try to request the file from the Gitea API - if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, string(giteaRoot), giteaApiToken, targetOptions) { + if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions) { returnErrorPage(ctx, ctx.Response.StatusCode()) } } @@ -155,7 +155,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { log.Debug().Msg("raw domain preparations, now trying with specified branch") if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], - string(giteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() @@ -167,7 +167,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp } else { log.Debug().Msg("raw domain preparations, now trying with default branch") tryBranch(targetRepo, "", pathElements[2:], - string(giteaRoot)+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() @@ -266,7 +266,7 @@ func Handler(mainDomainSuffix, rawDomain, giteaRoot []byte, rawInfoPage, giteaAp // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), string(giteaRoot), giteaApiToken) + canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken) if !valid { returnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return diff --git a/server/helpers.go b/server/helpers.go index 354f15e..ecb4bf8 100644 --- a/server/helpers.go +++ b/server/helpers.go @@ -3,8 +3,6 @@ package server import ( "bytes" "encoding/gob" - "os" - "github.com/akrylysov/pogreb" ) @@ -56,12 +54,3 @@ func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { } return true } - -// EnvOr reads an environment variable and returns a default value if it's empty. -// TODO: to helpers.go or use CLI framework -func EnvOr(env string, or string) string { - if v := os.Getenv(env); v != "" { - return v - } - return or -} From 5b81a8b8bc1869b78563bca34c59e2b2cfd81407 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 03:34:50 +0100 Subject: [PATCH 065/320] remove os.Getenv() usage --- cmd/flags.go | 34 +++++++++++++++++++++++++++++- cmd/main.go | 19 +++++++++++++---- server/certificates.go | 48 +++++++++++++++++++----------------------- 3 files changed, 70 insertions(+), 31 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 209228e..a631492 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -45,6 +45,7 @@ var ServeFlags = []cli.Flag{ Value: "https://docs.codeberg.org/pages/raw-content/", }, + // Server &cli.StringFlag{ Name: "host", Usage: "specifies host of listening address", @@ -57,8 +58,13 @@ var ServeFlags = []cli.Flag{ EnvVars: []string{"PORT"}, Value: "443", }, + &cli.BoolFlag{ + Name: "enable-http-server", + // TODO: desc + EnvVars: []string{"ENABLE_HTTP_SERVER"}, + }, - // ACME_API + // ACME &cli.StringFlag{ Name: "acme-api", EnvVars: []string{"ACME_API"}, @@ -69,4 +75,30 @@ var ServeFlags = []cli.Flag{ EnvVars: []string{"ACME_EMAIL"}, Value: "noreply@example.email", }, + &cli.BoolFlag{ + Name: "acme-use-rate-limits", + // TODO: Usage + EnvVars: []string{"ACME_USE_RATE_LIMITS"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "acme-accept-terms", + // TODO: Usage + EnvVars: []string{"ACME_ACCEPT_TERMS"}, + }, + &cli.StringFlag{ + Name: "acme-eab-kid", + // TODO: Usage + EnvVars: []string{"ACME_EAB_KID"}, + }, + &cli.StringFlag{ + Name: "acme-eab-hmac", + // TODO: Usage + EnvVars: []string{"ACME_EAB_HMAC"}, + }, + &cli.StringFlag{ + Name: "dns-provider", + // TODO: Usage + EnvVars: []string{"DNS_PROVIDER"}, + }, } diff --git a/cmd/main.go b/cmd/main.go index a46fdbc..be33940 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,10 +3,10 @@ package cmd import ( "bytes" "crypto/tls" + "errors" "fmt" "net" "net/http" - "os" "strings" "time" @@ -37,8 +37,19 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix := []byte(ctx.String("main-domain-suffix")) rawInfoPage := ctx.String("raw-info-page") listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) + enableHTTPServer := ctx.Bool("enable-http-server") + acmeAPI := ctx.String("acme-api") acmeMail := ctx.String("acme-email") + acmeUseRateLimits := ctx.Bool("acme-use-rate-limits") + acmeAcceptTerms := ctx.Bool("acme-accept-terms") + acmeEabKID := ctx.String("acme-eab-kid") + acmeEabHmac := ctx.String("acme-eab-hmac") + dnsProvider := ctx.String("dns-provider") + if acmeAcceptTerms || (dnsProvider == "" && acmeAPI != "https://acme.mock.directory") { + return errors.New("you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory") + } + allowedCorsDomains := AllowedCorsDomains if len(rawDomain) != 0 { allowedCorsDomains = append(allowedCorsDomains, []byte(rawDomain)) @@ -72,10 +83,10 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("couldn't create listener: %s", err) } - listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, giteaRoot, giteaAPIToken)) + listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits)) - server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail) - if os.Getenv("ENABLE_HTTP_SERVER") == "true" { + server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer) + if enableHTTPServer { go (func() { challengePath := []byte("/.well-known/acme-challenge/") err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { diff --git a/server/certificates.go b/server/certificates.go index 7b3e9d9..8ac158d 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -38,7 +38,7 @@ import ( ) // TlsConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken string) *tls.Config { +func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -94,13 +94,13 @@ func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken string) *tls.Co var tlsCertificate tls.Certificate var err error var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix); !ok { + if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits); !ok { // request a new certificate if bytes.Equal(sniBytes, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, mainDomainSuffix) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits) if err != nil { return nil, err } @@ -209,7 +209,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix []byte) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} if !PogrebGet(KeyDatabase, sni, res) { @@ -240,7 +240,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte) (tls.Certificate, bool) { } go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", mainDomainSuffix) + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) } @@ -253,9 +253,9 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte) (tls.Certificate, bool) { var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, mainDomainSuffix []byte) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") - if os.Getenv("DNS_PROVIDER") == "" && len(domains[0]) > 0 && domains[0][0] == '*' { + if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] } @@ -266,7 +266,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re time.Sleep(100 * time.Millisecond) _, working = obtainLocks.Load(name) } - cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix) + cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix, dnsProvider, acmeUseRateLimits) if !ok { return tls.Certificate{}, errors.New("certificate failed in synchronous request") } @@ -282,7 +282,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re var res *certificate.Resource var err error if renew != nil && renew.CertURL != "" { - if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { + if acmeUseRateLimits { acmeClientRequestLimit.Take() } log.Printf("Renewing certificate for %v", domains) @@ -299,7 +299,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } } - if os.Getenv("ACME_USE_RATE_LIMITS") != "false" { + if acmeUseRateLimits { acmeClientOrderLimit.Take() acmeClientRequestLimit.Take() } @@ -399,13 +399,9 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { return tlsCertificate } -func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { +func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool) { if KeyDatabaseErr != nil { - panic(KeyDatabaseErr) - } - - if os.Getenv("ACME_ACCEPT_TERMS") != "true" || (os.Getenv("DNS_PROVIDER") == "" && os.Getenv("ACME_API") != "https://acme.mock.directory") { - panic(errors.New("you must set ACME_ACCEPT_TERMS and DNS_PROVIDER, unless ACME_API is set to https://acme.mock.directory")) + panic(KeyDatabaseErr) // TODO: move it into own init and not panic on a unrelated topic!!!! } // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits @@ -449,8 +445,8 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { // accept terms & log in to EAB - if os.Getenv("ACME_EAB_KID") == "" || os.Getenv("ACME_EAB_HMAC") == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true"}) + if acmeEabKID == "" || acmeEabHmac == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) if err != nil { log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) } else { @@ -458,9 +454,9 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { } } else { reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: os.Getenv("ACME_ACCEPT_TERMS") == "true", - Kid: os.Getenv("ACME_EAB_KID"), - HmacEncoded: os.Getenv("ACME_EAB_HMAC"), + TermsOfServiceAgreed: acmeAcceptTerms, + Kid: acmeEabKID, + HmacEncoded: acmeEabHmac, }) if err != nil { log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) @@ -494,7 +490,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { if err != nil { log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) } - if os.Getenv("ENABLE_HTTP_SERVER") == "true" { + if enableHTTPServer { err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{}) if err != nil { log.Printf("[ERROR] Can't create HTTP-01 provider: %s", err) @@ -506,14 +502,14 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { if err != nil { log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { - if os.Getenv("DNS_PROVIDER") == "" { + if dnsProvider == "" { // using mock server, don't use wildcard certs err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) if err != nil { log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) } } else { - provider, err := dns.NewDNSChallengeProviderByName(os.Getenv("DNS_PROVIDER")) + provider, err := dns.NewDNSChallengeProviderByName(dnsProvider) if err != nil { log.Printf("[ERROR] Can't create DNS Challenge provider: %s", err) } @@ -525,7 +521,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { } if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", mainDomainSuffix) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) if err != nil { log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } @@ -590,7 +586,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail string) { // renew main certificate 30 days before it expires if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", mainDomainSuffix) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) if err != nil { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) } From 690879440a05d7969c3d86eae4f26847c8766075 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 03:44:21 +0100 Subject: [PATCH 066/320] move helper func in related packages --- cmd/main.go | 3 ++- server/certificates.go | 24 ++++++++++++---------- server/database/helpers.go | 38 +++++++++++++++++++++++++++++++++++ server/handler.go | 3 ++- server/helpers.go | 41 -------------------------------------- server/utils/utils.go | 11 ++++++++++ 6 files changed, 66 insertions(+), 54 deletions(-) create mode 100644 server/database/helpers.go create mode 100644 server/utils/utils.go diff --git a/cmd/main.go b/cmd/main.go index be33940..a7d606d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ import ( "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server" + "codeberg.org/codeberg/pages/server/utils" ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. @@ -91,7 +92,7 @@ func Serve(ctx *cli.Context) error { challengePath := []byte("/.well-known/acme-challenge/") err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := server.ChallengeCache.Get(string(server.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + challenge, ok := server.ChallengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) if !ok || challenge == nil { ctx.SetStatusCode(http.StatusNotFound) ctx.SetBodyString("no challenge for this token") diff --git a/server/certificates.go b/server/certificates.go index 8ac158d..5339375 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -14,12 +14,6 @@ import ( "encoding/json" "encoding/pem" "errors" - "github.com/OrlovEvgeny/go-mcache" - "github.com/akrylysov/pogreb/fs" - "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/challenge" - "github.com/go-acme/lego/v4/challenge/tlsalpn01" - "github.com/go-acme/lego/v4/providers/dns" "io/ioutil" "log" "math/big" @@ -29,12 +23,20 @@ import ( "sync" "time" + "github.com/OrlovEvgeny/go-mcache" "github.com/akrylysov/pogreb" + "github.com/akrylysov/pogreb/fs" "github.com/reugn/equalizer" "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" + + "codeberg.org/codeberg/pages/server/database" ) // TlsConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. @@ -212,7 +214,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} - if !PogrebGet(KeyDatabase, sni, res) { + if !database.PogrebGet(KeyDatabase, sni, res) { return tls.Certificate{}, false } @@ -317,7 +319,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) - PogrebPut(KeyDatabase, []byte(name), renew) + database.PogrebPut(KeyDatabase, []byte(name), renew) return tlsCertificate, nil } } @@ -325,7 +327,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } log.Printf("Obtained certificate for %v", domains) - PogrebPut(KeyDatabase, []byte(name), res) + database.PogrebPut(KeyDatabase, []byte(name), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -390,7 +392,7 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { databaseName = mainDomainSuffix } - PogrebPut(KeyDatabase, []byte(databaseName), res) + database.PogrebPut(KeyDatabase, []byte(databaseName), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -578,7 +580,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, // update main cert res := &certificate.Resource{} - if !PogrebGet(KeyDatabase, mainDomainSuffix, res) { + if !database.PogrebGet(KeyDatabase, mainDomainSuffix, res) { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) diff --git a/server/database/helpers.go b/server/database/helpers.go new file mode 100644 index 0000000..b2eb017 --- /dev/null +++ b/server/database/helpers.go @@ -0,0 +1,38 @@ +package database + +import ( + "bytes" + "encoding/gob" + "github.com/akrylysov/pogreb" +) + +func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { + var resGob bytes.Buffer + resEnc := gob.NewEncoder(&resGob) + err := resEnc.Encode(obj) + if err != nil { + panic(err) + } + err = db.Put(name, resGob.Bytes()) + if err != nil { + panic(err) + } +} + +func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { + resBytes, err := db.Get(name) + if err != nil { + panic(err) + } + if resBytes == nil { + return false + } + + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + err = resDec.Decode(obj) + if err != nil { + panic(err) + } + return true +} diff --git a/server/handler.go b/server/handler.go index ca2872f..80c1c79 100644 --- a/server/handler.go +++ b/server/handler.go @@ -16,6 +16,7 @@ import ( "github.com/valyala/fastjson" "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/utils" ) // Handler handles a single HTTP request to the web server. @@ -31,7 +32,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Enable browser caching for up to 10 minutes ctx.Response.Header.Set("Cache-Control", "public, max-age=600") - trimmedHost := TrimHostPort(ctx.Request.Host()) + trimmedHost := utils.TrimHostPort(ctx.Request.Host()) // Add HSTS for RawDomain and MainDomainSuffix if hsts := GetHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { diff --git a/server/helpers.go b/server/helpers.go index ecb4bf8..6d55ddf 100644 --- a/server/helpers.go +++ b/server/helpers.go @@ -2,8 +2,6 @@ package server import ( "bytes" - "encoding/gob" - "github.com/akrylysov/pogreb" ) // GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty @@ -15,42 +13,3 @@ func GetHSTSHeader(host, mainDomainSuffix, rawDomain []byte) string { return "" } } - -func TrimHostPort(host []byte) []byte { - i := bytes.IndexByte(host, ':') - if i >= 0 { - return host[:i] - } - return host -} - -func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { - var resGob bytes.Buffer - resEnc := gob.NewEncoder(&resGob) - err := resEnc.Encode(obj) - if err != nil { - panic(err) - } - err = db.Put(name, resGob.Bytes()) - if err != nil { - panic(err) - } -} - -func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { - resBytes, err := db.Get(name) - if err != nil { - panic(err) - } - if resBytes == nil { - return false - } - - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - err = resDec.Decode(obj) - if err != nil { - panic(err) - } - return true -} diff --git a/server/utils/utils.go b/server/utils/utils.go new file mode 100644 index 0000000..7be330f --- /dev/null +++ b/server/utils/utils.go @@ -0,0 +1,11 @@ +package utils + +import "bytes" + +func TrimHostPort(host []byte) []byte { + i := bytes.IndexByte(host, ':') + if i >= 0 { + return host[:i] + } + return host +} From 796f24262ecb4b7679b45e9c14ba0b30a59eb2a5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 03:46:21 +0100 Subject: [PATCH 067/320] fix code format --- server/handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/handler_test.go b/server/handler_test.go index 78357e7..c772dfc 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -11,7 +11,7 @@ func TestHandlerPerformance(t *testing.T) { testHandler := Handler( []byte("codeberg.page"), []byte("raw.codeberg.org"), - []byte("https://codeberg.org"), + "https://codeberg.org", "https://docs.codeberg.org/pages/raw-content/", "", [][]byte{[]byte("/.well-known/acme-challenge/")}, From 5ca5020cfa78811c4784f405dd633791c46ac081 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 04:15:48 +0100 Subject: [PATCH 068/320] open key-database deterministic --- cmd/certs.go | 14 +++++--- cmd/main.go | 14 ++++++-- server/cache/interface.go | 8 +++++ server/cache/setup.go | 7 ++++ server/certificates.go | 63 ++++++++++++++---------------------- server/database/helpers.go | 5 ++- server/database/interface.go | 12 +++++++ server/database/setup.go | 19 +++++++++++ 8 files changed, 94 insertions(+), 48 deletions(-) create mode 100644 server/cache/interface.go create mode 100644 server/cache/setup.go create mode 100644 server/database/interface.go create mode 100644 server/database/setup.go diff --git a/cmd/certs.go b/cmd/certs.go index 4676520..89521da 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -1,11 +1,12 @@ package cmd import ( + "fmt" "os" "github.com/urfave/cli/v2" - pages_server "codeberg.org/codeberg/pages/server" + "codeberg.org/codeberg/pages/server/database" ) var Certs = &cli.Command{ @@ -23,15 +24,18 @@ func certs(ctx *cli.Context) error { domains := ctx.Args().Slice()[2:] - if pages_server.KeyDatabaseErr != nil { - panic(pages_server.KeyDatabaseErr) + // TODO: make "key-database.pogreb" set via flag + keyDatabase, err := database.New("key-database.pogreb") + if err != nil { + return fmt.Errorf("could not create database: %v", err) } + for _, domain := range domains { - if err := pages_server.KeyDatabase.Delete([]byte(domain)); err != nil { + if err := keyDatabase.Delete([]byte(domain)); err != nil { panic(err) } } - if err := pages_server.KeyDatabase.Sync(); err != nil { + if err := keyDatabase.Sync(); err != nil { panic(err) } os.Exit(0) diff --git a/cmd/main.go b/cmd/main.go index a7d606d..2f3d7ac 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,8 @@ import ( "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/utils" ) @@ -84,9 +86,17 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("couldn't create listener: %s", err) } - listener = tls.NewListener(listener, server.TlsConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits)) - server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer) + // TODO: make "key-database.pogreb" set via flag + keyDatabase, err := database.New("key-database.pogreb") + if err != nil { + return fmt.Errorf("could not create database: %v", err) + } + + keyCache := cache.NewKeyValueCache() + listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, keyDatabase)) + + server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, keyDatabase) if enableHTTPServer { go (func() { challengePath := []byte("/.well-known/acme-challenge/") diff --git a/server/cache/interface.go b/server/cache/interface.go new file mode 100644 index 0000000..37ae8f5 --- /dev/null +++ b/server/cache/interface.go @@ -0,0 +1,8 @@ +package cache + +import "time" + +type SetGetKey interface { + Set(key string, value interface{}, ttl time.Duration) error + Get(key string) (interface{}, bool) +} diff --git a/server/cache/setup.go b/server/cache/setup.go new file mode 100644 index 0000000..a5928b0 --- /dev/null +++ b/server/cache/setup.go @@ -0,0 +1,7 @@ +package cache + +import "github.com/OrlovEvgeny/go-mcache" + +func NewKeyValueCache() SetGetKey { + return mcache.New() +} diff --git a/server/certificates.go b/server/certificates.go index 5339375..d6b6b86 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -24,8 +24,6 @@ import ( "time" "github.com/OrlovEvgeny/go-mcache" - "github.com/akrylysov/pogreb" - "github.com/akrylysov/pogreb/fs" "github.com/reugn/equalizer" "github.com/go-acme/lego/v4/certcrypto" @@ -36,11 +34,12 @@ import ( "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" + "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/database" ) -// TlsConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool) *tls.Config { +// TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. +func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache cache.SetGetKey, keyDatabase database.KeyDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -96,13 +95,13 @@ func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider st var tlsCertificate tls.Certificate var err error var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits); !ok { + if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase); !ok { // request a new certificate if bytes.Equal(sniBytes, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) if err != nil { return nil, err } @@ -134,14 +133,6 @@ func TlsConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider st } } -// TODO: clean up & move to init -var keyCache = mcache.New() -var KeyDatabase, KeyDatabaseErr = pogreb.Open("key-database.pogreb", &pogreb.Options{ - BackgroundSyncInterval: 30 * time.Second, - BackgroundCompactionInterval: 6 * time.Hour, - FileSystem: fs.OSMMap, -}) - func CheckUserLimit(user string) error { userLimit, ok := acmeClientCertificateLimitPerUser[user] if !ok { @@ -211,10 +202,10 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} - if !database.PogrebGet(KeyDatabase, sni, res) { + if !database.PogrebGet(keyDatabase, sni, res) { return tls.Certificate{}, false } @@ -242,7 +233,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs } go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) } @@ -255,7 +246,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -268,7 +259,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re time.Sleep(100 * time.Millisecond) _, working = obtainLocks.Load(name) } - cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix, dnsProvider, acmeUseRateLimits) + cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) if !ok { return tls.Certificate{}, errors.New("certificate failed in synchronous request") } @@ -277,7 +268,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re defer obtainLocks.Delete(name) if acmeClient == nil { - return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", string(mainDomainSuffix)), nil + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", string(mainDomainSuffix), keyDatabase), nil } // request actual cert @@ -319,15 +310,15 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) - database.PogrebPut(KeyDatabase, []byte(name), renew) + database.PogrebPut(keyDatabase, []byte(name), renew) return tlsCertificate, nil } } - return mockCert(domains[0], err.Error(), string(mainDomainSuffix)), err + return mockCert(domains[0], err.Error(), string(mainDomainSuffix), keyDatabase), err } log.Printf("Obtained certificate for %v", domains) - database.PogrebPut(KeyDatabase, []byte(name), res) + database.PogrebPut(keyDatabase, []byte(name), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -335,7 +326,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return tlsCertificate, nil } -func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { +func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) tls.Certificate { key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) if err != nil { panic(err) @@ -392,7 +383,7 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { databaseName = mainDomainSuffix } - database.PogrebPut(KeyDatabase, []byte(databaseName), res) + database.PogrebPut(keyDatabase, []byte(databaseName), res) tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { @@ -401,13 +392,9 @@ func mockCert(domain, msg, mainDomainSuffix string) tls.Certificate { return tlsCertificate } -func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool) { - if KeyDatabaseErr != nil { - panic(KeyDatabaseErr) // TODO: move it into own init and not panic on a unrelated topic!!!! - } - +func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool, keyDatabase database.KeyDB) { // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits - mainCertBytes, err := KeyDatabase.Get(mainDomainSuffix) + mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) if err != nil { // key database is not working panic(err) @@ -523,7 +510,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, } if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) if err != nil { log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } @@ -531,7 +518,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, go (func() { for { - err := KeyDatabase.Sync() + err := keyDatabase.Sync() if err != nil { log.Printf("[ERROR] Syncing key database failed: %s", err) } @@ -544,7 +531,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, // clean up expired certs now := time.Now() expiredCertCount := 0 - keyDatabaseIterator := KeyDatabase.Items() + keyDatabaseIterator := keyDatabase.Items() key, resBytes, err := keyDatabaseIterator.Next() for err == nil { if !bytes.Equal(key, mainDomainSuffix) { @@ -558,7 +545,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := KeyDatabase.Delete(key) + err := keyDatabase.Delete(key) if err != nil { log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) } else { @@ -571,7 +558,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) // compact the database - result, err := KeyDatabase.Compact() + result, err := keyDatabase.Compact() if err != nil { log.Printf("[ERROR] Compacting key database failed: %s", err) } else { @@ -580,7 +567,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, // update main cert res := &certificate.Resource{} - if !database.PogrebGet(KeyDatabase, mainDomainSuffix, res) { + if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) @@ -588,7 +575,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, // renew main certificate 30 days before it expires if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) if err != nil { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) } diff --git a/server/database/helpers.go b/server/database/helpers.go index b2eb017..98ea3fa 100644 --- a/server/database/helpers.go +++ b/server/database/helpers.go @@ -3,10 +3,9 @@ package database import ( "bytes" "encoding/gob" - "github.com/akrylysov/pogreb" ) -func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { +func PogrebPut(db KeyDB, name []byte, obj interface{}) { var resGob bytes.Buffer resEnc := gob.NewEncoder(&resGob) err := resEnc.Encode(obj) @@ -19,7 +18,7 @@ func PogrebPut(db *pogreb.DB, name []byte, obj interface{}) { } } -func PogrebGet(db *pogreb.DB, name []byte, obj interface{}) bool { +func PogrebGet(db KeyDB, name []byte, obj interface{}) bool { resBytes, err := db.Get(name) if err != nil { panic(err) diff --git a/server/database/interface.go b/server/database/interface.go new file mode 100644 index 0000000..2b582ae --- /dev/null +++ b/server/database/interface.go @@ -0,0 +1,12 @@ +package database + +import "github.com/akrylysov/pogreb" + +type KeyDB interface { + Sync() error + Put(key []byte, value []byte) error + Get(key []byte) ([]byte, error) + Delete(key []byte) error + Compact() (pogreb.CompactionResult, error) + Items() *pogreb.ItemIterator +} diff --git a/server/database/setup.go b/server/database/setup.go new file mode 100644 index 0000000..c16ff36 --- /dev/null +++ b/server/database/setup.go @@ -0,0 +1,19 @@ +package database + +import ( + "fmt" + "github.com/akrylysov/pogreb" + "github.com/akrylysov/pogreb/fs" + "time" +) + +func New(path string) (KeyDB, error) { + if path == "" { + return nil, fmt.Errorf("path not set") + } + return pogreb.Open(path, &pogreb.Options{ + BackgroundSyncInterval: 30 * time.Second, + BackgroundCompactionInterval: 6 * time.Hour, + FileSystem: fs.OSMMap, + }) +} From 0bc38b668f557e07834a283a3064cecceeaf8ff1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 04:18:53 +0100 Subject: [PATCH 069/320] db Sync on exit --- cmd/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/main.go b/cmd/main.go index 2f3d7ac..ff778d1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -92,6 +92,7 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("could not create database: %v", err) } + defer keyDatabase.Sync() // database has no close ... sync behave like it keyCache := cache.NewKeyValueCache() listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, keyDatabase)) From f35c4d0f667cb2304f1dabf020c7e3f46664dae8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 3 Dec 2021 04:32:30 +0100 Subject: [PATCH 070/320] make mem cache an interface and inject --- cmd/main.go | 7 ++++--- server/cache/interface.go | 1 + server/certificates.go | 33 ++++++++++++++++++--------------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index ff778d1..98b65d2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -95,15 +95,16 @@ func Serve(ctx *cli.Context) error { defer keyDatabase.Sync() // database has no close ... sync behave like it keyCache := cache.NewKeyValueCache() - listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, keyDatabase)) + challengeCache := cache.NewKeyValueCache() + listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, challengeCache, keyDatabase)) - server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, keyDatabase) + server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) if enableHTTPServer { go (func() { challengePath := []byte("/.well-known/acme-challenge/") err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := server.ChallengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + challenge, ok := challengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) if !ok || challenge == nil { ctx.SetStatusCode(http.StatusNotFound) ctx.SetBodyString("no challenge for this token") diff --git a/server/cache/interface.go b/server/cache/interface.go index 37ae8f5..2952b29 100644 --- a/server/cache/interface.go +++ b/server/cache/interface.go @@ -5,4 +5,5 @@ import "time" type SetGetKey interface { Set(key string, value interface{}, ttl time.Duration) error Get(key string) (interface{}, bool) + Remove(key string) } diff --git a/server/certificates.go b/server/certificates.go index d6b6b86..12d42a1 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -23,7 +23,6 @@ import ( "sync" "time" - "github.com/OrlovEvgeny/go-mcache" "github.com/reugn/equalizer" "github.com/go-acme/lego/v4/certcrypto" @@ -39,7 +38,7 @@ import ( ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache cache.SetGetKey, keyDatabase database.KeyDB) *tls.Config { +func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -52,7 +51,7 @@ func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider st if info.SupportedProtos != nil { for _, proto := range info.SupportedProtos { if proto == tlsalpn01.ACMETLS1Protocol { - challenge, ok := ChallengeCache.Get(sni) + challenge, ok := challengeCache.Get(sni) if !ok { return nil, errors.New("no challenge for this domain") } @@ -176,29 +175,33 @@ var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) // rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) var acmeClientRequestLimit = equalizer.NewTokenBucket(5, 1*time.Second) -var ChallengeCache = mcache.New() - -type AcmeTLSChallengeProvider struct{} +type AcmeTLSChallengeProvider struct { + challengeCache cache.SetGetKey +} +// make sure AcmeTLSChallengeProvider match Provider interface var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { - return ChallengeCache.Set(domain, keyAuth, 1*time.Hour) + return a.challengeCache.Set(domain, keyAuth, 1*time.Hour) } func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { - ChallengeCache.Remove(domain) + a.challengeCache.Remove(domain) return nil } -type AcmeHTTPChallengeProvider struct{} +type AcmeHTTPChallengeProvider struct { + challengeCache cache.SetGetKey +} +// make sure AcmeHTTPChallengeProvider match Provider interface var _ challenge.Provider = AcmeHTTPChallengeProvider{} func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { - return ChallengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) + return a.challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) } func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { - ChallengeCache.Remove(domain + "/" + token) + a.challengeCache.Remove(domain + "/" + token) return nil } @@ -392,7 +395,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) return tlsCertificate } -func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool, keyDatabase database.KeyDB) { +func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) { // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) if err != nil { @@ -475,12 +478,12 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, if err != nil { log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { - err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) } if enableHTTPServer { - err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{}) + err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) if err != nil { log.Printf("[ERROR] Can't create HTTP-01 provider: %s", err) } @@ -493,7 +496,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, } else { if dnsProvider == "" { // using mock server, don't use wildcard certs - err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{}) + err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) } From 38426c26db6e461a678ff8e344f24a6e7bef38d6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 14:47:33 +0100 Subject: [PATCH 071/320] move upstream into own package --- html/error.go | 23 +++ server/domains.go | 9 +- server/handler.go | 303 +++--------------------------------- server/upstream/2rm.go | 10 ++ server/upstream/const.go | 18 +++ server/upstream/helper.go | 53 +++++++ server/upstream/upstream.go | 193 +++++++++++++++++++++++ 7 files changed, 321 insertions(+), 288 deletions(-) create mode 100644 html/error.go create mode 100644 server/upstream/2rm.go create mode 100644 server/upstream/const.go create mode 100644 server/upstream/helper.go create mode 100644 server/upstream/upstream.go diff --git a/html/error.go b/html/error.go new file mode 100644 index 0000000..f831443 --- /dev/null +++ b/html/error.go @@ -0,0 +1,23 @@ +package html + +import ( + "bytes" + "github.com/valyala/fasthttp" + "strconv" +) + +// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced +// with the provided status code. +func ReturnErrorPage(ctx *fasthttp.RequestCtx, code int) { + ctx.Response.SetStatusCode(code) + ctx.Response.Header.SetContentType("text/html; charset=utf-8") + message := fasthttp.StatusMessage(code) + if code == fasthttp.StatusMisdirectedRequest { + message += " - domain not specified in .domains file" + } + if code == fasthttp.StatusFailedDependency { + message += " - target repo/branch doesn't exist or is private" + } + // TODO: use template engine? + ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) +} diff --git a/server/domains.go b/server/domains.go index 5673960..5aeae96 100644 --- a/server/domains.go +++ b/server/domains.go @@ -1,11 +1,14 @@ package server import ( - "github.com/OrlovEvgeny/go-mcache" - "github.com/valyala/fasthttp" "net" "strings" "time" + + "github.com/OrlovEvgeny/go-mcache" + "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/server/upstream" ) // DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. @@ -84,7 +87,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) res := fasthttp.AcquireResponse() - err := upstreamClient.Do(req, res) + err := upstream.Client.Do(req, res) if err == nil && res.StatusCode() == fasthttp.StatusOK { for _, domain := range strings.Split(string(res.Body()), "\n") { domain = strings.ToLower(domain) diff --git a/server/handler.go b/server/handler.go index 80c1c79..a6a19f4 100644 --- a/server/handler.go +++ b/server/handler.go @@ -2,21 +2,13 @@ package server import ( "bytes" - "fmt" - "io" - "mime" - "path" - "strconv" "strings" - "time" - - "github.com/OrlovEvgeny/go-mcache" - "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/upstream" "codeberg.org/codeberg/pages/server/utils" + "github.com/rs/zerolog/log" + "github.com/valyala/fasthttp" ) // Handler handles a single HTTP request to the web server. @@ -49,7 +41,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Block blacklisted paths (like ACME challenges) for _, blacklistedPath := range blacklistedPaths { if bytes.HasPrefix(ctx.Path(), blacklistedPath) { - returnErrorPage(ctx, fasthttp.StatusForbidden) + html.ReturnErrorPage(ctx, fasthttp.StatusForbidden) return } } @@ -74,7 +66,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Prepare request information to Gitea var targetOwner, targetRepo, targetBranch, targetPath string - var targetOptions = &upstreamOptions{ + var targetOptions = &upstream.Options{ ForbiddenMimeTypes: map[string]struct{}{}, TryIndexPages: true, } @@ -87,7 +79,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp } // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := getBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken) + branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken) if branchTimestampResult == nil { // branch doesn't exist return false @@ -96,9 +88,9 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Branch exists, use it targetRepo = repo targetPath = strings.Trim(strings.Join(path, "/"), "/") - targetBranch = branchTimestampResult.branch + targetBranch = branchTimestampResult.Branch - targetOptions.BranchTimestamp = branchTimestampResult.timestamp + targetOptions.BranchTimestamp = branchTimestampResult.Timestamp if canonicalLink != "" { // Hide from search machines & add canonical link @@ -128,8 +120,8 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp } // Try to request the file from the Gitea API - if !upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions) { - returnErrorPage(ctx, ctx.Response.StatusCode()) + if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions) { + html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) } } @@ -163,7 +155,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp return } log.Debug().Msg("missing branch") - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { log.Debug().Msg("raw domain preparations, now trying with default branch") @@ -206,7 +198,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } return } @@ -219,7 +211,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp log.Debug().Msg("tryBranch, now trying upstream") tryUpstream() } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } return } @@ -244,7 +236,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp } // Couldn't find a valid repo/branch - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { trimmedHostStr := string(trimmedHost) @@ -252,7 +244,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Serve pages from external domains targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix)) if targetOwner == "" { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } @@ -269,7 +261,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken) if !valid { - returnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) + html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! @@ -278,7 +270,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } } @@ -287,268 +279,9 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp tryUpstream() return } else { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } } } } - -// returnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced -// with the provided status code. -func returnErrorPage(ctx *fasthttp.RequestCtx, code int) { - ctx.Response.SetStatusCode(code) - ctx.Response.Header.SetContentType("text/html; charset=utf-8") - message := fasthttp.StatusMessage(code) - if code == fasthttp.StatusMisdirectedRequest { - message += " - domain not specified in .domains file" - } - if code == fasthttp.StatusFailedDependency { - message += " - target repo/branch doesn't exist or is private" - } - // TODO: use template engine? - ctx.Response.SetBody(bytes.ReplaceAll(html.NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) -} - -// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var DefaultBranchCacheTimeout = 15 * time.Minute - -// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter -// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be -// picked up faster, while still allowing the content to be cached longer if nothing changes. -var BranchExistanceCacheTimeout = 5 * time.Minute - -// branchTimestampCache stores branch timestamps for faster cache checking -var branchTimestampCache = mcache.New() - -type branchTimestamp struct { - branch string - timestamp time.Time -} - -// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending -// on your available memory. -var FileCacheTimeout = 5 * time.Minute - -// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. -var FileCacheSizeLimit = 1024 * 1024 - -// fileResponseCache stores responses from the Gitea server -// TODO: make this an MRU cache with a size limit -var fileResponseCache = mcache.New() - -type fileResponse struct { - exists bool - mimeType string - body []byte -} - -// getBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch -// (or nil if the branch doesn't exist) -func getBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string) *branchTimestamp { - if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { - if result == nil { - return nil - } - return result.(*branchTimestamp) - } - result := &branchTimestamp{} - result.branch = branch - if branch == "" { - // Get default branch - var body = make([]byte, 0) - // TODO: use header for API key? - status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) - return nil - } - result.branch = fastjson.GetString(body, "default_branch") - } - - var body = make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { - return nil - } - - result.timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) - return result -} - -var upstreamClient = fasthttp.Client{ - ReadTimeout: 10 * time.Second, - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! -} - -// upstreamIndexPages lists pages that may be considered as index pages for directories. -var upstreamIndexPages = []string{ - "index.html", -} - -// upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *upstreamOptions) (final bool) { - log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() - - if options.ForbiddenMimeTypes == nil { - options.ForbiddenMimeTypes = map[string]struct{}{} - } - - // Check if the branch exists and when it was modified - if options.BranchTimestamp == (time.Time{}) { - branch := getBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken) - - if branch == nil { - returnErrorPage(ctx, fasthttp.StatusFailedDependency) - return true - } - targetBranch = branch.branch - options.BranchTimestamp = branch.timestamp - } - - if targetOwner == "" || targetRepo == "" || targetBranch == "" { - returnErrorPage(ctx, fasthttp.StatusBadRequest) - return true - } - - // Check if the browser has a cached version - if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { - if !ifModifiedSince.Before(options.BranchTimestamp) { - ctx.Response.SetStatusCode(fasthttp.StatusNotModified) - return true - } - } - log.Debug().Msg("preparations") - - // Make a GET request to the upstream URL - uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath - var req *fasthttp.Request - var res *fasthttp.Response - var cachedResponse fileResponse - var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { - cachedResponse = cachedValue.(fileResponse) - } else { - req = fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaApiToken) - res = fasthttp.AcquireResponse() - res.SetBodyStream(&strings.Reader{}, -1) - err = upstreamClient.Do(req, res) - } - log.Debug().Msg("acquisition") - - // Handle errors - if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { - if options.TryIndexPages { - // copy the options struct & try if an index page exists - optionsForIndexPages := *options - optionsForIndexPages.TryIndexPages = false - optionsForIndexPages.AppendTrailingSlash = true - for _, indexPage := range upstreamIndexPages { - if upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - return true - } - } - // compatibility fix for GitHub Pages (/example → /example.html) - optionsForIndexPages.AppendTrailingSlash = false - optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - return true - } - } - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) - if res != nil { - // Update cache if the request is fresh - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, - }, FileCacheTimeout) - } - return false - } - if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { - fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - - // Append trailing slash if missing (for index files), and redirect to fix filenames in general - // options.AppendTrailingSlash is only true when looking for index pages - if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { - ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) - return true - } - if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { - ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) - return true - } - if options.RedirectIfExists != "" { - ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) - return true - } - log.Debug().Msg("error handling") - - // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(targetPath)) - mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { - if options.DefaultMimeType != "" { - mimeType = options.DefaultMimeType - } else { - mimeType = "application/octet-stream" - } - } - ctx.Response.Header.SetContentType(mimeType) - - // Everything's okay so far - ctx.Response.SetStatusCode(fasthttp.StatusOK) - ctx.Response.Header.SetLastModified(options.BranchTimestamp) - - log.Debug().Msg("response preparations") - - // Write the response body to the original request - var cacheBodyWriter bytes.Buffer - if res != nil { - if res.Header.ContentLength() > FileCacheSizeLimit { - err = res.BodyWriteTo(ctx.Response.BodyWriter()) - } else { - // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? - err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) - } - } else { - _, err = ctx.Write(cachedResponse.body) - } - if err != nil { - fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) - returnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true - } - log.Debug().Msg("response") - - if res != nil && ctx.Err() == nil { - cachedResponse.exists = true - cachedResponse.mimeType = mimeType - cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) - } - - return true -} - -// upstreamOptions provides various options for the upstream request. -type upstreamOptions struct { - DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} - TryIndexPages bool - AppendTrailingSlash bool - RedirectIfExists string - BranchTimestamp time.Time -} diff --git a/server/upstream/2rm.go b/server/upstream/2rm.go new file mode 100644 index 0000000..f02b452 --- /dev/null +++ b/server/upstream/2rm.go @@ -0,0 +1,10 @@ +package upstream + +import "github.com/OrlovEvgeny/go-mcache" + +// branchTimestampCache stores branch timestamps for faster cache checking +var branchTimestampCache = mcache.New() + +// fileResponseCache stores responses from the Gitea server +// TODO: make this an MRU cache with a size limit +var fileResponseCache = mcache.New() diff --git a/server/upstream/const.go b/server/upstream/const.go new file mode 100644 index 0000000..f51b152 --- /dev/null +++ b/server/upstream/const.go @@ -0,0 +1,18 @@ +package upstream + +import "time" + +// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. +var DefaultBranchCacheTimeout = 15 * time.Minute + +// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter +// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be +// picked up faster, while still allowing the content to be cached longer if nothing changes. +var BranchExistanceCacheTimeout = 5 * time.Minute + +// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending +// on your available memory. +var FileCacheTimeout = 5 * time.Minute + +// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. +var FileCacheSizeLimit = 1024 * 1024 diff --git a/server/upstream/helper.go b/server/upstream/helper.go new file mode 100644 index 0000000..eeda2c2 --- /dev/null +++ b/server/upstream/helper.go @@ -0,0 +1,53 @@ +package upstream + +import ( + "time" + + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" +) + +type branchTimestamp struct { + Branch string + Timestamp time.Time +} + +// GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch +// (or nil if the branch doesn't exist) +func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string) *branchTimestamp { + if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { + if result == nil { + return nil + } + return result.(*branchTimestamp) + } + result := &branchTimestamp{} + result.Branch = branch + if branch == "" { + // Get default branch + var body = make([]byte, 0) + // TODO: use header for API key? + status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) + if err != nil || status != 200 { + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) + return nil + } + result.Branch = fastjson.GetString(body, "default_branch") + } + + var body = make([]byte, 0) + status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) + if err != nil || status != 200 { + return nil + } + + result.Timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) + return result +} + +type fileResponse struct { + exists bool + mimeType string + body []byte +} diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go new file mode 100644 index 0000000..a269e06 --- /dev/null +++ b/server/upstream/upstream.go @@ -0,0 +1,193 @@ +package upstream + +import ( + "bytes" + "fmt" + "io" + "mime" + "path" + "strconv" + "strings" + "time" + + "codeberg.org/codeberg/pages/html" + + "github.com/rs/zerolog/log" + "github.com/valyala/fasthttp" +) + +// upstreamIndexPages lists pages that may be considered as index pages for directories. +var upstreamIndexPages = []string{ + "index.html", +} + +// Options provides various options for the upstream request. +type Options struct { + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool + AppendTrailingSlash bool + RedirectIfExists string + BranchTimestamp time.Time +} + +var Client = fasthttp.Client{ + ReadTimeout: 10 * time.Second, + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! +} + +// Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. +func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options) (final bool) { + log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() + + if options.ForbiddenMimeTypes == nil { + options.ForbiddenMimeTypes = map[string]struct{}{} + } + + // Check if the branch exists and when it was modified + if options.BranchTimestamp == (time.Time{}) { + branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken) + + if branch == nil { + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + return true + } + targetBranch = branch.Branch + options.BranchTimestamp = branch.Timestamp + } + + if targetOwner == "" || targetRepo == "" || targetBranch == "" { + html.ReturnErrorPage(ctx, fasthttp.StatusBadRequest) + return true + } + + // Check if the browser has a cached version + if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { + if !ifModifiedSince.Before(options.BranchTimestamp) { + ctx.Response.SetStatusCode(fasthttp.StatusNotModified) + return true + } + } + log.Debug().Msg("preparations") + + // Make a GET request to the upstream URL + uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath + var req *fasthttp.Request + var res *fasthttp.Response + var cachedResponse fileResponse + var err error + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { + cachedResponse = cachedValue.(fileResponse) + } else { + req = fasthttp.AcquireRequest() + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaApiToken) + res = fasthttp.AcquireResponse() + res.SetBodyStream(&strings.Reader{}, -1) + err = Client.Do(req, res) + } + log.Debug().Msg("acquisition") + + // Handle errors + if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { + if options.TryIndexPages { + // copy the options struct & try if an index page exists + optionsForIndexPages := *options + optionsForIndexPages.TryIndexPages = false + optionsForIndexPages.AppendTrailingSlash = true + for _, indexPage := range upstreamIndexPages { + if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + return true + } + } + // compatibility fix for GitHub Pages (/example → /example.html) + optionsForIndexPages.AppendTrailingSlash = false + optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" + if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + return true + } + } + ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + if res != nil { + // Update cache if the request is fresh + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + exists: false, + }, FileCacheTimeout) + } + return false + } + if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { + fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) + html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + + // Append trailing slash if missing (for index files), and redirect to fix filenames in general + // options.AppendTrailingSlash is only true when looking for index pages + if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { + ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) + return true + } + if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { + ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) + return true + } + if options.RedirectIfExists != "" { + ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) + return true + } + log.Debug().Msg("error handling") + + // Set the MIME type + mimeType := mime.TypeByExtension(path.Ext(targetPath)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { + if options.DefaultMimeType != "" { + mimeType = options.DefaultMimeType + } else { + mimeType = "application/octet-stream" + } + } + ctx.Response.Header.SetContentType(mimeType) + + // Everything's okay so far + ctx.Response.SetStatusCode(fasthttp.StatusOK) + ctx.Response.Header.SetLastModified(options.BranchTimestamp) + + log.Debug().Msg("response preparations") + + // Write the response body to the original request + var cacheBodyWriter bytes.Buffer + if res != nil { + if res.Header.ContentLength() > FileCacheSizeLimit { + err = res.BodyWriteTo(ctx.Response.BodyWriter()) + } else { + // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? + err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + } + } else { + _, err = ctx.Write(cachedResponse.body) + } + if err != nil { + fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) + html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) + return true + } + log.Debug().Msg("response") + + if res != nil && ctx.Err() == nil { + cachedResponse.exists = true + cachedResponse.mimeType = mimeType + cachedResponse.body = cacheBodyWriter.Bytes() + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) + } + + return true +} From 5b2e91a37a93789639ae63e9493810b07d824b0c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 4 Dec 2021 21:15:51 +0100 Subject: [PATCH 072/320] REDIRECT_RAW_INFO -> RAW_INFO_PAGE --- README.md | 2 +- cmd/flags.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 044379e..ec87cc1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. -- `REDIRECT_RAW_INFO` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. +- `RAW_INFO_PAGE` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. - `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. diff --git a/cmd/flags.go b/cmd/flags.go index a631492..57cddda 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -41,7 +41,7 @@ var ServeFlags = []cli.Flag{ &cli.StringFlag{ Name: "raw-info-page", Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", - EnvVars: []string{"REDIRECT_RAW_INFO"}, + EnvVars: []string{"RAW_INFO_PAGE"}, Value: "https://docs.codeberg.org/pages/raw-content/", }, From fdd04610e5d37bfa960503ce06e7407dd40aec43 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 4 Dec 2021 21:16:22 +0100 Subject: [PATCH 073/320] fix .domains and make it redable --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ec87cc1..616d69d 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,12 @@ - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. - +``` // Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. // // Mapping custom domains is not static anymore, but can be done with DNS: // -// 1) add a "domains.txt" text file to your repository, containing the allowed domains, separated by new lines. The +// 1) add a ".domains" text file to your repository, containing the allowed domains, separated by new lines. The // first line will be the canonical domain/URL; all other occurrences will be redirected to it. // // 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to @@ -33,3 +33,4 @@ // example.org IN ALIAS codeberg.page. // // Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. +``` \ No newline at end of file From 97d4ea9d6b50b65a2dbf5673b466c94312e73543 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 4 Dec 2021 21:17:52 +0100 Subject: [PATCH 074/320] main-domain-suffix -> pages-domain --- cmd/flags.go | 2 +- cmd/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 57cddda..944e86e 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -9,7 +9,7 @@ var ServeFlags = []cli.Flag{ // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". &cli.StringFlag{ - Name: "main-domain-suffix", + Name: "pages-domain", Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", EnvVars: []string{"PAGES_DOMAIN"}, Value: "codeberg.page", diff --git a/cmd/main.go b/cmd/main.go index 98b65d2..1d1cede 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -37,7 +37,7 @@ func Serve(ctx *cli.Context) error { giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") giteaAPIToken := ctx.String("gitea-api-token") rawDomain := ctx.String("raw-domain") - mainDomainSuffix := []byte(ctx.String("main-domain-suffix")) + mainDomainSuffix := []byte(ctx.String("pages-domain")) rawInfoPage := ctx.String("raw-info-page") listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) enableHTTPServer := ctx.Bool("enable-http-server") From b28204a468b23dd36bc956c7ce56cd6d3c90fffc Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 4 Dec 2021 21:21:27 +0100 Subject: [PATCH 075/320] acme-api -> acme-api-endpoint --- cmd/flags.go | 2 +- cmd/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 944e86e..6381ee5 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -66,7 +66,7 @@ var ServeFlags = []cli.Flag{ // ACME &cli.StringFlag{ - Name: "acme-api", + Name: "acme-api-endpoint", EnvVars: []string{"ACME_API"}, Value: "https://acme-v02.api.letsencrypt.org/directory", }, diff --git a/cmd/main.go b/cmd/main.go index 1d1cede..fcc2b83 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -42,7 +42,7 @@ func Serve(ctx *cli.Context) error { listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) enableHTTPServer := ctx.Bool("enable-http-server") - acmeAPI := ctx.String("acme-api") + acmeAPI := ctx.String("acme-api-endpoint") acmeMail := ctx.String("acme-email") acmeUseRateLimits := ctx.Bool("acme-use-rate-limits") acmeAcceptTerms := ctx.Bool("acme-accept-terms") From 2b49039252755ec1a982374825ab3cbc6a4da806 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 4 Dec 2021 21:59:04 +0100 Subject: [PATCH 076/320] add todo --- cmd/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/main.go b/cmd/main.go index fcc2b83..7fab2d3 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -28,6 +28,7 @@ var AllowedCorsDomains = [][]byte{ } // BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. +// TODO: Make it a flag too var BlacklistedPaths = [][]byte{ []byte("/.well-known/acme-challenge/"), } From b3830e979c663a4a9cacbd672cfd86a3f1687ae8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:02:44 +0100 Subject: [PATCH 077/320] inject all cache --- cmd/main.go | 22 +++++++++++++++++----- server/certificates.go | 10 +++++++--- server/domains.go | 12 +++--------- server/handler.go | 21 +++++++++++++-------- server/handler_test.go | 4 ++++ 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 7fab2d3..e40e385 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -64,8 +64,18 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix = append([]byte{'.'}, mainDomainSuffix...) } + keyCache := cache.NewKeyValueCache() + challengeCache := cache.NewKeyValueCache() + // canonicalDomainCache stores canonical domains + var canonicalDomainCache = cache.NewKeyValueCache() + // dnsLookupCache stores DNS lookups for custom domains + var dnsLookupCache = cache.NewKeyValueCache() + // Create handler based on settings - handler := server.Handler(mainDomainSuffix, []byte(rawDomain), giteaRoot, rawInfoPage, giteaAPIToken, BlacklistedPaths, allowedCorsDomains) + handler := server.Handler(mainDomainSuffix, []byte(rawDomain), + giteaRoot, rawInfoPage, giteaAPIToken, + BlacklistedPaths, allowedCorsDomains, + dnsLookupCache, canonicalDomainCache) // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) @@ -93,11 +103,13 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("could not create database: %v", err) } - defer keyDatabase.Sync() // database has no close ... sync behave like it + defer keyDatabase.Sync() //nolint:errcheck // database has no close ... sync behave like it - keyCache := cache.NewKeyValueCache() - challengeCache := cache.NewKeyValueCache() - listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, challengeCache, keyDatabase)) + listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, + giteaRoot, giteaAPIToken, dnsProvider, + acmeUseRateLimits, + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, + keyDatabase)) server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) if enableHTTPServer { diff --git a/server/certificates.go b/server/certificates.go index 12d42a1..1493d03 100644 --- a/server/certificates.go +++ b/server/certificates.go @@ -38,7 +38,11 @@ import ( ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) *tls.Config { +func TLSConfig(mainDomainSuffix []byte, + giteaRoot, giteaApiToken, dnsProvider string, + acmeUseRateLimits bool, + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, + keyDatabase database.KeyDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -71,14 +75,14 @@ func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider st sni = string(sniBytes) } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix)) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs sniBytes = mainDomainSuffix sni = string(sniBytes) } else { _, _ = targetRepo, targetBranch - _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken) + _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { sniBytes = mainDomainSuffix sni = string(sniBytes) diff --git a/server/domains.go b/server/domains.go index 5aeae96..cbbdef9 100644 --- a/server/domains.go +++ b/server/domains.go @@ -5,21 +5,18 @@ import ( "strings" "time" - "github.com/OrlovEvgeny/go-mcache" "github.com/valyala/fasthttp" + "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/upstream" ) // DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. var DnsLookupCacheTimeout = 15 * time.Minute -// dnsLookupCache stores DNS lookups for custom domains -var dnsLookupCache = mcache.New() - // getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func getTargetFromDNS(domain, mainDomainSuffix string) (targetOwner, targetRepo, targetBranch string) { +func getTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error @@ -68,11 +65,8 @@ func getTargetFromDNS(domain, mainDomainSuffix string) (targetOwner, targetRepo, // CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. var CanonicalDomainCacheTimeout = 15 * time.Minute -// canonicalDomainCache stores canonical domains -var canonicalDomainCache = mcache.New() - // checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string) (canonicalDomain string, valid bool) { +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string, canonicalDomainCache cache.SetGetKey) (canonicalDomain string, valid bool) { domains := []string{} if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) diff --git a/server/handler.go b/server/handler.go index a6a19f4..ba6cfdd 100644 --- a/server/handler.go +++ b/server/handler.go @@ -4,15 +4,20 @@ import ( "bytes" "strings" - "codeberg.org/codeberg/pages/html" - "codeberg.org/codeberg/pages/server/upstream" - "codeberg.org/codeberg/pages/server/utils" "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/upstream" + "codeberg.org/codeberg/pages/server/utils" ) // Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte) func(ctx *fasthttp.RequestCtx) { +func Handler(mainDomainSuffix, rawDomain []byte, + giteaRoot, rawInfoPage, giteaApiToken string, + blacklistedPaths, allowedCorsDomains [][]byte, + dnsLookupCache, canonicalDomainCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() @@ -108,7 +113,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp var tryUpstream = func() { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken) + canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -242,7 +247,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp trimmedHostStr := string(trimmedHost) // Serve pages from external domains - targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix)) + targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return @@ -259,13 +264,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAp // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken) + canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) + targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return diff --git a/server/handler_test.go b/server/handler_test.go index c772dfc..3488ae7 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -5,6 +5,8 @@ import ( "github.com/valyala/fasthttp" "testing" "time" + + "codeberg.org/codeberg/pages/server/cache" ) func TestHandlerPerformance(t *testing.T) { @@ -16,6 +18,8 @@ func TestHandlerPerformance(t *testing.T) { "", [][]byte{[]byte("/.well-known/acme-challenge/")}, [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, + cache.NewKeyValueCache(), + cache.NewKeyValueCache(), ) ctx := &fasthttp.RequestCtx{ From bb6f28fe570793872ab2b60a8ff706459f3e1feb Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:09:21 +0100 Subject: [PATCH 078/320] move setup of fastServer into own func --- cmd/main.go | 15 +-------------- server/setup.go | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 server/setup.go diff --git a/cmd/main.go b/cmd/main.go index e40e385..f4ee205 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,7 +8,6 @@ import ( "net" "net/http" "strings" - "time" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" @@ -77,19 +76,7 @@ func Serve(ctx *cli.Context) error { BlacklistedPaths, allowedCorsDomains, dnsLookupCache, canonicalDomainCache) - // Enable compression by wrapping the handler with the compression function provided by FastHTTP - compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) - - fastServer := &fasthttp.Server{ - Handler: compressedHandler, - DisablePreParseMultipartForm: true, - MaxRequestBodySize: 0, - NoDefaultServerHeader: true, - NoDefaultDate: true, - ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge - Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! - MaxConnsPerIP: 100, - } + fastServer, err := server.SetupServer(handler) // Setup listener and TLS log.Info().Msgf("Listening on https://%s", listeningAddress) diff --git a/server/setup.go b/server/setup.go new file mode 100644 index 0000000..6986c7c --- /dev/null +++ b/server/setup.go @@ -0,0 +1,25 @@ +package server + +import ( + "time" + + "github.com/valyala/fasthttp" +) + +func SetupServer(handler fasthttp.RequestHandler) (*fasthttp.Server, error) { + // Enable compression by wrapping the handler with the compression function provided by FastHTTP + compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) + + fastServer := &fasthttp.Server{ + Handler: compressedHandler, + DisablePreParseMultipartForm: true, + MaxRequestBodySize: 0, + NoDefaultServerHeader: true, + NoDefaultDate: true, + ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge + Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! + MaxConnsPerIP: 100, + } + + return fastServer, nil +} From ccada3e6dfd1b306922ce697b9aec1ff834b1a3a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:21:05 +0100 Subject: [PATCH 079/320] split cert func to related packages --- cmd/main.go | 5 +- server/{ => certificates}/certificates.go | 8 +- server/dns/const.go | 6 ++ server/dns/dns.go | 56 +++++++++++ server/domains.go | 110 ---------------------- server/handler.go | 9 +- server/upstream/const.go | 3 + server/upstream/domains.go | 53 +++++++++++ 8 files changed, 131 insertions(+), 119 deletions(-) rename server/{ => certificates}/certificates.go (97%) create mode 100644 server/dns/const.go create mode 100644 server/dns/dns.go delete mode 100644 server/domains.go create mode 100644 server/upstream/domains.go diff --git a/cmd/main.go b/cmd/main.go index f4ee205..246d0d7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ import ( "codeberg.org/codeberg/pages/server" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/utils" ) @@ -92,13 +93,13 @@ func Serve(ctx *cli.Context) error { } defer keyDatabase.Sync() //nolint:errcheck // database has no close ... sync behave like it - listener = tls.NewListener(listener, server.TLSConfig(mainDomainSuffix, + listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, keyDatabase)) - server.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) + certificates.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) if enableHTTPServer { go (func() { challengePath := []byte("/.well-known/acme-challenge/") diff --git a/server/certificates.go b/server/certificates/certificates.go similarity index 97% rename from server/certificates.go rename to server/certificates/certificates.go index 1493d03..b63ed0e 100644 --- a/server/certificates.go +++ b/server/certificates/certificates.go @@ -1,4 +1,4 @@ -package server +package certificates import ( "bytes" @@ -35,6 +35,8 @@ import ( "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/database" + dnsutils "codeberg.org/codeberg/pages/server/dns" + "codeberg.org/codeberg/pages/server/upstream" ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. @@ -75,14 +77,14 @@ func TLSConfig(mainDomainSuffix []byte, sni = string(sniBytes) } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = getTargetFromDNS(sni, string(mainDomainSuffix), dnsLookupCache) + targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(sni, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs sniBytes = mainDomainSuffix sni = string(sniBytes) } else { _, _ = targetRepo, targetBranch - _, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) + _, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { sniBytes = mainDomainSuffix sni = string(sniBytes) diff --git a/server/dns/const.go b/server/dns/const.go new file mode 100644 index 0000000..fcf669b --- /dev/null +++ b/server/dns/const.go @@ -0,0 +1,6 @@ +package dns + +import "time" + +// DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. +var DnsLookupCacheTimeout = 15 * time.Minute diff --git a/server/dns/dns.go b/server/dns/dns.go new file mode 100644 index 0000000..c231439 --- /dev/null +++ b/server/dns/dns.go @@ -0,0 +1,56 @@ +package dns + +import ( + "net" + "strings" + + "codeberg.org/codeberg/pages/server/cache" +) + +// GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. +// If everything is fine, it returns the target data. +func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { + // Get CNAME or TXT + var cname string + var err error + if cachedName, ok := dnsLookupCache.Get(domain); ok { + cname = cachedName.(string) + } else { + cname, err = net.LookupCNAME(domain) + cname = strings.TrimSuffix(cname, ".") + if err != nil || !strings.HasSuffix(cname, mainDomainSuffix) { + cname = "" + // TODO: check if the A record matches! + names, err := net.LookupTXT(domain) + if err == nil { + for _, name := range names { + name = strings.TrimSuffix(name, ".") + if strings.HasSuffix(name, mainDomainSuffix) { + cname = name + break + } + } + } + } + _ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout) + } + if cname == "" { + return + } + cnameParts := strings.Split(strings.TrimSuffix(cname, mainDomainSuffix), ".") + targetOwner = cnameParts[len(cnameParts)-1] + if len(cnameParts) > 1 { + targetRepo = cnameParts[len(cnameParts)-2] + } + if len(cnameParts) > 2 { + targetBranch = cnameParts[len(cnameParts)-3] + } + if targetRepo == "" { + targetRepo = "pages" + } + if targetBranch == "" && targetRepo != "pages" { + targetBranch = "pages" + } + // if targetBranch is still empty, the caller must find the default branch + return +} diff --git a/server/domains.go b/server/domains.go deleted file mode 100644 index cbbdef9..0000000 --- a/server/domains.go +++ /dev/null @@ -1,110 +0,0 @@ -package server - -import ( - "net" - "strings" - "time" - - "github.com/valyala/fasthttp" - - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/upstream" -) - -// DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. -var DnsLookupCacheTimeout = 15 * time.Minute - -// getTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. -// If everything is fine, it returns the target data. -func getTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { - // Get CNAME or TXT - var cname string - var err error - if cachedName, ok := dnsLookupCache.Get(domain); ok { - cname = cachedName.(string) - } else { - cname, err = net.LookupCNAME(domain) - cname = strings.TrimSuffix(cname, ".") - if err != nil || !strings.HasSuffix(cname, mainDomainSuffix) { - cname = "" - // TODO: check if the A record matches! - names, err := net.LookupTXT(domain) - if err == nil { - for _, name := range names { - name = strings.TrimSuffix(name, ".") - if strings.HasSuffix(name, mainDomainSuffix) { - cname = name - break - } - } - } - } - _ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout) - } - if cname == "" { - return - } - cnameParts := strings.Split(strings.TrimSuffix(cname, mainDomainSuffix), ".") - targetOwner = cnameParts[len(cnameParts)-1] - if len(cnameParts) > 1 { - targetRepo = cnameParts[len(cnameParts)-2] - } - if len(cnameParts) > 2 { - targetBranch = cnameParts[len(cnameParts)-3] - } - if targetRepo == "" { - targetRepo = "pages" - } - if targetBranch == "" && targetRepo != "pages" { - targetBranch = "pages" - } - // if targetBranch is still empty, the caller must find the default branch - return -} - -// CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. -var CanonicalDomainCacheTimeout = 15 * time.Minute - -// checkCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string, canonicalDomainCache cache.SetGetKey) (canonicalDomain string, valid bool) { - domains := []string{} - if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { - domains = cachedValue.([]string) - for _, domain := range domains { - if domain == actualDomain { - valid = true - break - } - } - } else { - req := fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) - res := fasthttp.AcquireResponse() - - err := upstream.Client.Do(req, res) - if err == nil && res.StatusCode() == fasthttp.StatusOK { - for _, domain := range strings.Split(string(res.Body()), "\n") { - domain = strings.ToLower(domain) - domain = strings.TrimSpace(domain) - domain = strings.TrimPrefix(domain, "http://") - domain = strings.TrimPrefix(domain, "https://") - if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { - domains = append(domains, domain) - } - if domain == actualDomain { - valid = true - } - } - } - domains = append(domains, targetOwner+mainDomainSuffix) - if domains[len(domains)-1] == actualDomain { - valid = true - } - if targetRepo != "" && targetRepo != "pages" { - domains[len(domains)-1] += "/" + targetRepo - } - _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout) - } - canonicalDomain = domains[0] - return -} diff --git a/server/handler.go b/server/handler.go index ba6cfdd..89f3f4a 100644 --- a/server/handler.go +++ b/server/handler.go @@ -9,6 +9,7 @@ import ( "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/dns" "codeberg.org/codeberg/pages/server/upstream" "codeberg.org/codeberg/pages/server/utils" ) @@ -113,7 +114,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, var tryUpstream = func() { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) + canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -247,7 +248,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, trimmedHostStr := string(trimmedHost) // Serve pages from external domains - targetOwner, targetRepo, targetBranch = getTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) + targetOwner, targetRepo, targetBranch = dns.GetTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return @@ -264,13 +265,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := checkCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) + canonicalDomain, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = getTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) + targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return diff --git a/server/upstream/const.go b/server/upstream/const.go index f51b152..8c351df 100644 --- a/server/upstream/const.go +++ b/server/upstream/const.go @@ -16,3 +16,6 @@ var FileCacheTimeout = 5 * time.Minute // FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. var FileCacheSizeLimit = 1024 * 1024 + +// CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. +var CanonicalDomainCacheTimeout = 15 * time.Minute diff --git a/server/upstream/domains.go b/server/upstream/domains.go new file mode 100644 index 0000000..87941dd --- /dev/null +++ b/server/upstream/domains.go @@ -0,0 +1,53 @@ +package upstream + +import ( + "strings" + + "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/server/cache" +) + +// CheckCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). +func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string, canonicalDomainCache cache.SetGetKey) (canonicalDomain string, valid bool) { + domains := []string{} + if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { + domains = cachedValue.([]string) + for _, domain := range domains { + if domain == actualDomain { + valid = true + break + } + } + } else { + req := fasthttp.AcquireRequest() + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) + res := fasthttp.AcquireResponse() + + err := Client.Do(req, res) + if err == nil && res.StatusCode() == fasthttp.StatusOK { + for _, domain := range strings.Split(string(res.Body()), "\n") { + domain = strings.ToLower(domain) + domain = strings.TrimSpace(domain) + domain = strings.TrimPrefix(domain, "http://") + domain = strings.TrimPrefix(domain, "https://") + if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { + domains = append(domains, domain) + } + if domain == actualDomain { + valid = true + } + } + } + domains = append(domains, targetOwner+mainDomainSuffix) + if domains[len(domains)-1] == actualDomain { + valid = true + } + if targetRepo != "" && targetRepo != "pages" { + domains[len(domains)-1] += "/" + targetRepo + } + _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout) + } + canonicalDomain = domains[0] + return +} From b6c4c63fb4aeeb86e9401ccbd4a3fd6d0bc290e8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:25:12 +0100 Subject: [PATCH 080/320] own file --- server/certificates/certificates.go | 70 ------------------------ server/certificates/mock.go | 84 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 70 deletions(-) create mode 100644 server/certificates/mock.go diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index b63ed0e..afaa4f0 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -6,17 +6,13 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" - "crypto/x509/pkix" "encoding/gob" "encoding/json" - "encoding/pem" "errors" "io/ioutil" "log" - "math/big" "os" "strconv" "strings" @@ -335,72 +331,6 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return tlsCertificate, nil } -func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) tls.Certificate { - key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) - if err != nil { - panic(err) - } - - template := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: domain, - Organization: []string{"Codeberg Pages Error Certificate (couldn't obtain ACME certificate)"}, - OrganizationalUnit: []string{ - "Will not try again for 6 hours to avoid hitting rate limits for your domain.", - "Check https://docs.codeberg.org/codeberg-pages/troubleshooting/ for troubleshooting tips, and feel " + - "free to create an issue at https://codeberg.org/Codeberg/pages-server if you can't solve it.\n", - "Error message: " + msg, - }, - }, - - // certificates younger than 7 days are renewed, so this enforces the cert to not be renewed for a 6 hours - NotAfter: time.Now().Add(time.Hour*24*7 + time.Hour*6), - NotBefore: time.Now(), - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - certBytes, err := x509.CreateCertificate( - rand.Reader, - &template, - &template, - &key.(*rsa.PrivateKey).PublicKey, - key, - ) - if err != nil { - panic(err) - } - - out := &bytes.Buffer{} - err = pem.Encode(out, &pem.Block{ - Bytes: certBytes, - Type: "CERTIFICATE", - }) - if err != nil { - panic(err) - } - outBytes := out.Bytes() - res := &certificate.Resource{ - PrivateKey: certcrypto.PEMEncode(key), - Certificate: outBytes, - IssuerCertificate: outBytes, - Domain: domain, - } - databaseName := domain - if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { - databaseName = mainDomainSuffix - } - database.PogrebPut(keyDatabase, []byte(databaseName), res) - - tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) - if err != nil { - panic(err) - } - return tlsCertificate -} - func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) { // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) diff --git a/server/certificates/mock.go b/server/certificates/mock.go new file mode 100644 index 0000000..19adb92 --- /dev/null +++ b/server/certificates/mock.go @@ -0,0 +1,84 @@ +package certificates + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" + + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/certificate" + + "codeberg.org/codeberg/pages/server/database" +) + +func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) tls.Certificate { + key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) + if err != nil { + panic(err) + } + + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: domain, + Organization: []string{"Codeberg Pages Error Certificate (couldn't obtain ACME certificate)"}, + OrganizationalUnit: []string{ + "Will not try again for 6 hours to avoid hitting rate limits for your domain.", + "Check https://docs.codeberg.org/codeberg-pages/troubleshooting/ for troubleshooting tips, and feel " + + "free to create an issue at https://codeberg.org/Codeberg/pages-server if you can't solve it.\n", + "Error message: " + msg, + }, + }, + + // certificates younger than 7 days are renewed, so this enforces the cert to not be renewed for a 6 hours + NotAfter: time.Now().Add(time.Hour*24*7 + time.Hour*6), + NotBefore: time.Now(), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + certBytes, err := x509.CreateCertificate( + rand.Reader, + &template, + &template, + &key.(*rsa.PrivateKey).PublicKey, + key, + ) + if err != nil { + panic(err) + } + + out := &bytes.Buffer{} + err = pem.Encode(out, &pem.Block{ + Bytes: certBytes, + Type: "CERTIFICATE", + }) + if err != nil { + panic(err) + } + outBytes := out.Bytes() + res := &certificate.Resource{ + PrivateKey: certcrypto.PEMEncode(key), + Certificate: outBytes, + IssuerCertificate: outBytes, + Domain: domain, + } + databaseName := domain + if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { + databaseName = mainDomainSuffix + } + database.PogrebPut(keyDatabase, []byte(databaseName), res) + + tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) + if err != nil { + panic(err) + } + return tlsCertificate +} From 76c867cfcaacbe3c2ee900d286e3e68bf00bc84c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:45:22 +0100 Subject: [PATCH 081/320] move "http acme server setup" into own func --- cmd/main.go | 29 ++++++++--------------------- server/setup.go | 30 ++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 246d0d7..8aa56e0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,18 +6,15 @@ import ( "errors" "fmt" "net" - "net/http" "strings" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" - "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/database" - "codeberg.org/codeberg/pages/server/utils" ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. @@ -77,7 +74,8 @@ func Serve(ctx *cli.Context) error { BlacklistedPaths, allowedCorsDomains, dnsLookupCache, canonicalDomainCache) - fastServer, err := server.SetupServer(handler) + fastServer := server.SetupServer(handler) + httpServer := server.SetupHttpACMEChallengeServer(challengeCache) // Setup listener and TLS log.Info().Msgf("Listening on https://%s", listeningAddress) @@ -100,31 +98,20 @@ func Serve(ctx *cli.Context) error { keyDatabase)) certificates.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) + if enableHTTPServer { - go (func() { - challengePath := []byte("/.well-known/acme-challenge/") - err := fasthttp.ListenAndServe("[::]:80", func(ctx *fasthttp.RequestCtx) { - if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) - if !ok || challenge == nil { - ctx.SetStatusCode(http.StatusNotFound) - ctx.SetBodyString("no challenge for this token") - } - ctx.SetBodyString(challenge.(string)) - } else { - ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) - } - }) + go func() { + err := httpServer.ListenAndServe("[::]:80") if err != nil { - log.Fatal().Err(err).Msg("Couldn't start HTTP fastServer") + log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") } - })() + }() } // Start the web fastServer err = fastServer.Serve(listener) if err != nil { - log.Fatal().Err(err).Msg("Couldn't start fastServer") + log.Panic().Err(err).Msg("Couldn't start fastServer") } return nil diff --git a/server/setup.go b/server/setup.go index 6986c7c..546aba1 100644 --- a/server/setup.go +++ b/server/setup.go @@ -1,16 +1,21 @@ package server import ( + "bytes" + "net/http" "time" "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/utils" ) -func SetupServer(handler fasthttp.RequestHandler) (*fasthttp.Server, error) { +func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) - fastServer := &fasthttp.Server{ + return &fasthttp.Server{ Handler: compressedHandler, DisablePreParseMultipartForm: true, MaxRequestBodySize: 0, @@ -20,6 +25,23 @@ func SetupServer(handler fasthttp.RequestHandler) (*fasthttp.Server, error) { Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! MaxConnsPerIP: 100, } - - return fastServer, nil +} + +func SetupHttpACMEChallengeServer(challengeCache cache.SetGetKey) *fasthttp.Server { + challengePath := []byte("/.well-known/acme-challenge/") + + return &fasthttp.Server{ + Handler: func(ctx *fasthttp.RequestCtx) { + if bytes.HasPrefix(ctx.Path(), challengePath) { + challenge, ok := challengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) + if !ok || challenge == nil { + ctx.SetStatusCode(http.StatusNotFound) + ctx.SetBodyString("no challenge for this token") + } + ctx.SetBodyString(challenge.(string)) + } else { + ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) + } + }, + } } From de4706bf588c93ec03ca9d7a47d0e7c57a6b96f0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:53:46 +0100 Subject: [PATCH 082/320] rm 2rm --- cmd/main.go | 7 ++++++- server/handler.go | 6 +++--- server/handler_test.go | 2 ++ server/upstream/2rm.go | 10 ---------- server/upstream/helper.go | 4 +++- server/upstream/upstream.go | 12 +++++++----- 6 files changed, 21 insertions(+), 20 deletions(-) delete mode 100644 server/upstream/2rm.go diff --git a/cmd/main.go b/cmd/main.go index 8aa56e0..ceec3d5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -67,12 +67,17 @@ func Serve(ctx *cli.Context) error { var canonicalDomainCache = cache.NewKeyValueCache() // dnsLookupCache stores DNS lookups for custom domains var dnsLookupCache = cache.NewKeyValueCache() + // branchTimestampCache stores branch timestamps for faster cache checking + var branchTimestampCache = cache.NewKeyValueCache() + // fileResponseCache stores responses from the Gitea server + // TODO: make this an MRU cache with a size limit + var fileResponseCache = cache.NewKeyValueCache() // Create handler based on settings handler := server.Handler(mainDomainSuffix, []byte(rawDomain), giteaRoot, rawInfoPage, giteaAPIToken, BlacklistedPaths, allowedCorsDomains, - dnsLookupCache, canonicalDomainCache) + dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache) fastServer := server.SetupServer(handler) httpServer := server.SetupHttpACMEChallengeServer(challengeCache) diff --git a/server/handler.go b/server/handler.go index 89f3f4a..4b3c4ef 100644 --- a/server/handler.go +++ b/server/handler.go @@ -18,7 +18,7 @@ import ( func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaApiToken string, blacklistedPaths, allowedCorsDomains [][]byte, - dnsLookupCache, canonicalDomainCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { + dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() @@ -85,7 +85,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken) + branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken, branchTimestampCache) if branchTimestampResult == nil { // branch doesn't exist return false @@ -126,7 +126,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Try to request the file from the Gitea API - if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions) { + if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions, branchTimestampCache, fileResponseCache) { html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) } } diff --git a/server/handler_test.go b/server/handler_test.go index 3488ae7..0ec9fcd 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -20,6 +20,8 @@ func TestHandlerPerformance(t *testing.T) { [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, cache.NewKeyValueCache(), cache.NewKeyValueCache(), + cache.NewKeyValueCache(), + cache.NewKeyValueCache(), ) ctx := &fasthttp.RequestCtx{ diff --git a/server/upstream/2rm.go b/server/upstream/2rm.go deleted file mode 100644 index f02b452..0000000 --- a/server/upstream/2rm.go +++ /dev/null @@ -1,10 +0,0 @@ -package upstream - -import "github.com/OrlovEvgeny/go-mcache" - -// branchTimestampCache stores branch timestamps for faster cache checking -var branchTimestampCache = mcache.New() - -// fileResponseCache stores responses from the Gitea server -// TODO: make this an MRU cache with a size limit -var fileResponseCache = mcache.New() diff --git a/server/upstream/helper.go b/server/upstream/helper.go index eeda2c2..d8882b6 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -5,6 +5,8 @@ import ( "github.com/valyala/fasthttp" "github.com/valyala/fastjson" + + "codeberg.org/codeberg/pages/server/cache" ) type branchTimestamp struct { @@ -14,7 +16,7 @@ type branchTimestamp struct { // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) -func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string) *branchTimestamp { +func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { return nil diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index a269e06..3dfddb7 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -10,10 +10,12 @@ import ( "strings" "time" - "codeberg.org/codeberg/pages/html" "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" ) // upstreamIndexPages lists pages that may be considered as index pages for directories. @@ -39,7 +41,7 @@ var Client = fasthttp.Client{ } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options) (final bool) { +func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() if options.ForbiddenMimeTypes == nil { @@ -48,7 +50,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t // Check if the branch exists and when it was modified if options.BranchTimestamp == (time.Time{}) { - branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken) + branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken, branchTimestampCache) if branch == nil { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -97,7 +99,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t optionsForIndexPages.TryIndexPages = false optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range upstreamIndexPages { - if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages) { + if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) @@ -107,7 +109,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.AppendTrailingSlash = false optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages) { + if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) From e6198e4ddd32c32a9634ca0543445766e98e4848 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 15:59:43 +0100 Subject: [PATCH 083/320] start refactor Upstream func --- server/handler.go | 2 +- server/upstream/domains.go | 2 +- server/upstream/upstream.go | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server/handler.go b/server/handler.go index 4b3c4ef..e7652dd 100644 --- a/server/handler.go +++ b/server/handler.go @@ -126,7 +126,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Try to request the file from the Gitea API - if !upstream.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, targetOptions, branchTimestampCache, fileResponseCache) { + if !targetOptions.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) } } diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 87941dd..89bc7fb 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -24,7 +24,7 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) res := fasthttp.AcquireResponse() - err := Client.Do(req, res) + err := client.Do(req, res) if err == nil && res.StatusCode() == fasthttp.StatusOK { for _, domain := range strings.Split(string(res.Body()), "\n") { domain = strings.ToLower(domain) diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 3dfddb7..396f3c5 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -10,7 +10,6 @@ import ( "strings" "time" - "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" @@ -33,7 +32,7 @@ type Options struct { BranchTimestamp time.Time } -var Client = fasthttp.Client{ +var client = fasthttp.Client{ ReadTimeout: 10 * time.Second, MaxConnDuration: 60 * time.Second, MaxConnWaitTimeout: 1000 * time.Millisecond, @@ -41,7 +40,7 @@ var Client = fasthttp.Client{ } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, options *Options, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { +func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() if options.ForbiddenMimeTypes == nil { @@ -87,7 +86,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaApiToken) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) - err = Client.Do(req, res) + err = client.Do(req, res) } log.Debug().Msg("acquisition") @@ -99,7 +98,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t optionsForIndexPages.TryIndexPages = false optionsForIndexPages.AppendTrailingSlash = true for _, indexPage := range upstreamIndexPages { - if Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) { + if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) @@ -109,7 +108,7 @@ func Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, t // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.AppendTrailingSlash = false optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, &optionsForIndexPages, branchTimestampCache, fileResponseCache) { + if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, FileCacheTimeout) From 77e39b22132696cadb29b894a8a19ec8494d139d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 16:24:26 +0100 Subject: [PATCH 084/320] unexport if posible --- server/certificates/certificates.go | 4 ++-- server/dns/const.go | 4 ++-- server/dns/dns.go | 2 +- server/upstream/const.go | 22 +++++++++++----------- server/upstream/domains.go | 2 +- server/upstream/helper.go | 4 ++-- server/upstream/upstream.go | 10 +++++----- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index afaa4f0..fb61bd8 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -134,7 +134,7 @@ func TLSConfig(mainDomainSuffix []byte, } } -func CheckUserLimit(user string) error { +func checkUserLimit(user string) error { userLimit, ok := acmeClientCertificateLimitPerUser[user] if !ok { // Each Codeberg user can only add 10 new domains per day. @@ -292,7 +292,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } if res == nil { if user != "" { - if err := CheckUserLimit(user); err != nil { + if err := checkUserLimit(user); err != nil { return tls.Certificate{}, err } } diff --git a/server/dns/const.go b/server/dns/const.go index fcf669b..bb2413b 100644 --- a/server/dns/const.go +++ b/server/dns/const.go @@ -2,5 +2,5 @@ package dns import "time" -// DnsLookupCacheTimeout specifies the timeout for the DNS lookup cache. -var DnsLookupCacheTimeout = 15 * time.Minute +// lookupCacheTimeout specifies the timeout for the DNS lookup cache. +var lookupCacheTimeout = 15 * time.Minute diff --git a/server/dns/dns.go b/server/dns/dns.go index c231439..dc759b0 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -32,7 +32,7 @@ func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetG } } } - _ = dnsLookupCache.Set(domain, cname, DnsLookupCacheTimeout) + _ = dnsLookupCache.Set(domain, cname, lookupCacheTimeout) } if cname == "" { return diff --git a/server/upstream/const.go b/server/upstream/const.go index 8c351df..77f64dd 100644 --- a/server/upstream/const.go +++ b/server/upstream/const.go @@ -2,20 +2,20 @@ package upstream import "time" -// DefaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var DefaultBranchCacheTimeout = 15 * time.Minute +// defaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. +var defaultBranchCacheTimeout = 15 * time.Minute -// BranchExistanceCacheTimeout specifies the timeout for the branch timestamp & existance cache. It should be shorter -// than FileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be +// branchExistenceCacheTimeout specifies the timeout for the branch timestamp & existence cache. It should be shorter +// than fileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be // picked up faster, while still allowing the content to be cached longer if nothing changes. -var BranchExistanceCacheTimeout = 5 * time.Minute +var branchExistenceCacheTimeout = 5 * time.Minute -// FileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending +// fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending // on your available memory. -var FileCacheTimeout = 5 * time.Minute +var fileCacheTimeout = 5 * time.Minute -// FileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. -var FileCacheSizeLimit = 1024 * 1024 +// fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. +var fileCacheSizeLimit = 1024 * 1024 -// CanonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. -var CanonicalDomainCacheTimeout = 15 * time.Minute +// canonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. +var canonicalDomainCacheTimeout = 15 * time.Minute diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 89bc7fb..5971e13 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -46,7 +46,7 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m if targetRepo != "" && targetRepo != "pages" { domains[len(domains)-1] += "/" + targetRepo } - _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, CanonicalDomainCacheTimeout) + _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, canonicalDomainCacheTimeout) } canonicalDomain = domains[0] return diff --git a/server/upstream/helper.go b/server/upstream/helper.go index d8882b6..b5ee77a 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -31,7 +31,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, br // TODO: use header for API key? status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, DefaultBranchCacheTimeout) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, defaultBranchCacheTimeout) return nil } result.Branch = fastjson.GetString(body, "default_branch") @@ -44,7 +44,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, br } result.Timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, BranchExistanceCacheTimeout) + _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, branchExistenceCacheTimeout) return result } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 396f3c5..306e1db 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -101,7 +101,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, - }, FileCacheTimeout) + }, fileCacheTimeout) return true } } @@ -111,7 +111,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, - }, FileCacheTimeout) + }, fileCacheTimeout) return true } } @@ -120,7 +120,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Update cache if the request is fresh _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, - }, FileCacheTimeout) + }, fileCacheTimeout) } return false } @@ -167,7 +167,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Write the response body to the original request var cacheBodyWriter bytes.Buffer if res != nil { - if res.Header.ContentLength() > FileCacheSizeLimit { + if res.Header.ContentLength() > fileCacheSizeLimit { err = res.BodyWriteTo(ctx.Response.BodyWriter()) } else { // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? @@ -187,7 +187,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, FileCacheTimeout) + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, fileCacheTimeout) } return true From 11fa729686663018fc59edd3aba6b0b5b136f81f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 16:33:56 +0100 Subject: [PATCH 085/320] mv acme config setup into own func --- cmd/main.go | 7 +- server/certificates/acme_account.go | 27 +++++ server/certificates/certificates.go | 156 +++++++++++++--------------- 3 files changed, 106 insertions(+), 84 deletions(-) create mode 100644 server/certificates/acme_account.go diff --git a/cmd/main.go b/cmd/main.go index ceec3d5..222c295 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -102,7 +102,12 @@ func Serve(ctx *cli.Context) error { keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, keyDatabase)) - certificates.SetupCertificates(mainDomainSuffix, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer, challengeCache, keyDatabase) + acmeConfig, err := certificates.SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeAcceptTerms) + if err != nil { + return err + } + + certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, keyDatabase) if enableHTTPServer { go func() { diff --git a/server/certificates/acme_account.go b/server/certificates/acme_account.go new file mode 100644 index 0000000..2ee2e80 --- /dev/null +++ b/server/certificates/acme_account.go @@ -0,0 +1,27 @@ +package certificates + +import ( + "crypto" + + "github.com/go-acme/lego/v4/registration" +) + +type AcmeAccount struct { + Email string + Registration *registration.Resource + Key crypto.PrivateKey `json:"-"` + KeyPEM string `json:"Key"` +} + +// make sure AcmeAccount match User interface +var _ registration.User = &AcmeAccount{} + +func (u *AcmeAccount) GetEmail() string { + return u.Email +} +func (u AcmeAccount) GetRegistration() *registration.Resource { + return u.Registration +} +func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { + return u.Key +} diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index fb61bd8..b20f623 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -2,7 +2,6 @@ package certificates import ( "bytes" - "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -12,15 +11,12 @@ import ( "encoding/json" "errors" "io/ioutil" - "log" "os" "strconv" "strings" "sync" "time" - "github.com/reugn/equalizer" - "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge" @@ -28,6 +24,8 @@ import ( "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" + "github.com/reugn/equalizer" + "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/database" @@ -147,26 +145,6 @@ func checkUserLimit(user string) error { return nil } -var myAcmeAccount AcmeAccount -var myAcmeConfig *lego.Config - -type AcmeAccount struct { - Email string - Registration *registration.Resource - Key crypto.PrivateKey `json:"-"` - KeyPEM string `json:"Key"` -} - -func (u *AcmeAccount) GetEmail() string { - return u.Email -} -func (u AcmeAccount) GetRegistration() *registration.Resource { - return u.Registration -} -func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { - return u.Key -} - var acmeClient, mainDomainAcmeClient *lego.Client var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} @@ -331,15 +309,12 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return tlsCertificate, nil } -func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeUseRateLimits, acmeAcceptTerms, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) { - // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits - mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) - if err != nil { - // key database is not working - panic(err) - } +func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { + const configFile = "acme-account.json" + var myAcmeAccount AcmeAccount + var myAcmeConfig *lego.Config - if account, err := ioutil.ReadFile("acme-account.json"); err == nil { + if account, err := ioutil.ReadFile(configFile); err == nil { err = json.Unmarshal(account, &myAcmeAccount) if err != nil { panic(err) @@ -351,66 +326,81 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig.CADirURL = acmeAPI myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + + // Validate Config _, err := lego.NewClient(myAcmeConfig) if err != nil { + // TODO: should we fail hard instead? log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } - } else if os.IsNotExist(err) { - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - panic(err) - } - myAcmeAccount = AcmeAccount{ - Email: acmeMail, - Key: privateKey, - KeyPEM: string(certcrypto.PEMEncode(privateKey)), - } - myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = acmeAPI - myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - tempClient, err := lego.NewClient(myAcmeConfig) - if err != nil { - log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) - } else { - // accept terms & log in to EAB - if acmeEabKID == "" || acmeEabHmac == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) - if err != nil { - log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) - } else { - myAcmeAccount.Registration = reg - } - } else { - reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: acmeAcceptTerms, - Kid: acmeEabKID, - HmacEncoded: acmeEabHmac, - }) - if err != nil { - log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) - } else { - myAcmeAccount.Registration = reg - } - } + return myAcmeConfig, nil + } else if !os.IsNotExist(err) { + return nil, err + } - if myAcmeAccount.Registration != nil { - acmeAccountJson, err := json.Marshal(myAcmeAccount) - if err != nil { - log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) - select {} - } - err = ioutil.WriteFile("acme-account.json", acmeAccountJson, 0600) - if err != nil { - log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) - select {} - } + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + myAcmeAccount = AcmeAccount{ + Email: acmeMail, + Key: privateKey, + KeyPEM: string(certcrypto.PEMEncode(privateKey)), + } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) + myAcmeConfig.CADirURL = acmeAPI + myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + tempClient, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + } else { + // accept terms & log in to EAB + if acmeEabKID == "" || acmeEabHmac == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) + if err != nil { + log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + } else { + myAcmeAccount.Registration = reg + } + } else { + reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: acmeAcceptTerms, + Kid: acmeEabKID, + HmacEncoded: acmeEabHmac, + }) + if err != nil { + log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + } else { + myAcmeAccount.Registration = reg } } - } else { + + if myAcmeAccount.Registration != nil { + acmeAccountJson, err := json.Marshal(myAcmeAccount) + if err != nil { + log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) + select {} + } + err = ioutil.WriteFile(configFile, acmeAccountJson, 0600) + if err != nil { + log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) + select {} + } + } + } + + return myAcmeConfig, nil +} + +func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) { + // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits + mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) + if err != nil { + // key database is not working panic(err) } - acmeClient, err = lego.NewClient(myAcmeConfig) + acmeClient, err = lego.NewClient(acmeConfig) if err != nil { log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { @@ -426,7 +416,7 @@ func SetupCertificates(mainDomainSuffix []byte, acmeAPI, acmeMail, acmeEabHmac, } } - mainDomainAcmeClient, err = lego.NewClient(myAcmeConfig) + mainDomainAcmeClient, err = lego.NewClient(acmeConfig) if err != nil { log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) } else { From de439f9becccbb3f9cd5d32ba14a50b19294da8c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 17:42:53 +0100 Subject: [PATCH 086/320] wrap cert db and make sync gracefull --- cmd/certs.go | 2 +- cmd/main.go | 2 +- server/certificates/certificates.go | 8 +-- server/certificates/mock.go | 2 +- server/database/helpers.go | 4 +- server/database/interface.go | 4 +- server/database/setup.go | 87 ++++++++++++++++++++++++++++- 7 files changed, 95 insertions(+), 14 deletions(-) diff --git a/cmd/certs.go b/cmd/certs.go index 89521da..6603e55 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -35,7 +35,7 @@ func certs(ctx *cli.Context) error { panic(err) } } - if err := keyDatabase.Sync(); err != nil { + if err := keyDatabase.Close(); err != nil { panic(err) } os.Exit(0) diff --git a/cmd/main.go b/cmd/main.go index 222c295..7eb3fac 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -94,7 +94,7 @@ func Serve(ctx *cli.Context) error { if err != nil { return fmt.Errorf("could not create database: %v", err) } - defer keyDatabase.Sync() //nolint:errcheck // database has no close ... sync behave like it + defer keyDatabase.Close() //nolint:errcheck // database has no close ... sync behave like it listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index b20f623..d2f7c76 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -38,7 +38,7 @@ func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaApiToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, - keyDatabase database.KeyDB) *tls.Config { + keyDatabase database.CertDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -185,7 +185,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, bool) { // parse certificate from database res := &certificate.Resource{} if !database.PogrebGet(keyDatabase, sni, res) { @@ -229,7 +229,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool, keyDatabase database.KeyDB) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -392,7 +392,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce return myAcmeConfig, nil } -func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.KeyDB) { +func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.CertDB) { // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) if err != nil { diff --git a/server/certificates/mock.go b/server/certificates/mock.go index 19adb92..22d5470 100644 --- a/server/certificates/mock.go +++ b/server/certificates/mock.go @@ -17,7 +17,7 @@ import ( "codeberg.org/codeberg/pages/server/database" ) -func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.KeyDB) tls.Certificate { +func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) tls.Certificate { key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) if err != nil { panic(err) diff --git a/server/database/helpers.go b/server/database/helpers.go index 98ea3fa..ea3e899 100644 --- a/server/database/helpers.go +++ b/server/database/helpers.go @@ -5,7 +5,7 @@ import ( "encoding/gob" ) -func PogrebPut(db KeyDB, name []byte, obj interface{}) { +func PogrebPut(db CertDB, name []byte, obj interface{}) { var resGob bytes.Buffer resEnc := gob.NewEncoder(&resGob) err := resEnc.Encode(obj) @@ -18,7 +18,7 @@ func PogrebPut(db KeyDB, name []byte, obj interface{}) { } } -func PogrebGet(db KeyDB, name []byte, obj interface{}) bool { +func PogrebGet(db CertDB, name []byte, obj interface{}) bool { resBytes, err := db.Get(name) if err != nil { panic(err) diff --git a/server/database/interface.go b/server/database/interface.go index 2b582ae..80d74d3 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -2,8 +2,8 @@ package database import "github.com/akrylysov/pogreb" -type KeyDB interface { - Sync() error +type CertDB interface { + Close() error Put(key []byte, value []byte) error Get(key []byte) ([]byte, error) Delete(key []byte) error diff --git a/server/database/setup.go b/server/database/setup.go index c16ff36..f7eeafc 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -1,19 +1,100 @@ package database import ( + "context" "fmt" + "time" + + "github.com/rs/zerolog/log" + "github.com/akrylysov/pogreb" "github.com/akrylysov/pogreb/fs" - "time" ) -func New(path string) (KeyDB, error) { +type aDB struct { + ctx context.Context + cancel context.CancelFunc + intern *pogreb.DB + syncInterval time.Duration +} + +func (p aDB) Close() error { + p.cancel() + return p.intern.Sync() +} + +func (p aDB) Put(key []byte, value []byte) error { + return p.intern.Put(key, value) +} + +func (p aDB) Get(key []byte) ([]byte, error) { + return p.intern.Get(key) +} + +func (p aDB) Delete(key []byte) error { + return p.intern.Delete(key) +} + +func (p aDB) Compact() (pogreb.CompactionResult, error) { + return p.intern.Compact() +} + +func (p aDB) Items() *pogreb.ItemIterator { + return p.intern.Items() +} + +var _ CertDB = &aDB{} + +func (p aDB) sync() { + for { + err := p.intern.Sync() + if err != nil { + log.Err(err).Msg("Syncing cert database failed") + } + select { + case <-p.ctx.Done(): + return + case <-time.After(p.syncInterval): + } + } +} + +func (p aDB) compact() { + for { + err := p.intern.Sync() + if err != nil { + log.Err(err).Msg("Syncing cert database failed") + } + select { + case <-p.ctx.Done(): + return + case <-time.After(p.syncInterval): + } + } +} + +func New(path string) (CertDB, error) { if path == "" { return nil, fmt.Errorf("path not set") } - return pogreb.Open(path, &pogreb.Options{ + db, err := pogreb.Open(path, &pogreb.Options{ BackgroundSyncInterval: 30 * time.Second, BackgroundCompactionInterval: 6 * time.Hour, FileSystem: fs.OSMMap, }) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithCancel(context.Background()) + result := &aDB{ + ctx: ctx, + cancel: cancel, + intern: db, + syncInterval: 5 * time.Minute, + } + + go result.sync() + + return result, nil } From a0e0d2d335e219d6696c2b9d0c9e4f01be5f7c7b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 17:44:10 +0100 Subject: [PATCH 087/320] make certdb maintain go routine a own func --- cmd/main.go | 3 + server/certificates/certificates.go | 132 +++++++++++++--------------- 2 files changed, 64 insertions(+), 71 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 7eb3fac..b026885 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -109,6 +109,9 @@ func Serve(ctx *cli.Context) error { certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, keyDatabase) + // TODO: make it graceful + go certificates.MaintainCertDB(mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + if enableHTTPServer { go func() { err := httpServer.ListenAndServe("[::]:80") diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index d2f7c76..e484d78 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -444,75 +444,65 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } } - - go (func() { - for { - err := keyDatabase.Sync() - if err != nil { - log.Printf("[ERROR] Syncing key database failed: %s", err) - } - time.Sleep(5 * time.Minute) - // TODO: graceful exit - } - })() - go (func() { - for { - // clean up expired certs - now := time.Now() - expiredCertCount := 0 - keyDatabaseIterator := keyDatabase.Items() - key, resBytes, err := keyDatabaseIterator.Next() - for err == nil { - if !bytes.Equal(key, mainDomainSuffix) { - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - res := &certificate.Resource{} - err = resDec.Decode(res) - if err != nil { - panic(err) - } - - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := keyDatabase.Delete(key) - if err != nil { - log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) - } else { - expiredCertCount++ - } - } - } - key, resBytes, err = keyDatabaseIterator.Next() - } - log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) - - // compact the database - result, err := keyDatabase.Compact() - if err != nil { - log.Printf("[ERROR] Compacting key database failed: %s", err) - } else { - log.Printf("[INFO] Compacted key database (%+v)", result) - } - - // update main cert - res := &certificate.Resource{} - if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") - } else { - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - - // renew main certificate 30 days before it expires - if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { - go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) - if err != nil { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) - } - })() - } - } - - time.Sleep(12 * time.Hour) - } - })() +} + +func MaintainCertDB(mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) { + for { + // clean up expired certs + now := time.Now() + expiredCertCount := 0 + keyDatabaseIterator := keyDatabase.Items() + key, resBytes, err := keyDatabaseIterator.Next() + for err == nil { + if !bytes.Equal(key, mainDomainSuffix) { + resGob := bytes.NewBuffer(resBytes) + resDec := gob.NewDecoder(resGob) + res := &certificate.Resource{} + err = resDec.Decode(res) + if err != nil { + panic(err) + } + + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) + if err != nil || !tlsCertificates[0].NotAfter.After(now) { + err := keyDatabase.Delete(key) + if err != nil { + log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) + } else { + expiredCertCount++ + } + } + } + key, resBytes, err = keyDatabaseIterator.Next() + } + log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) + + // compact the database + result, err := keyDatabase.Compact() + if err != nil { + log.Printf("[ERROR] Compacting key database failed: %s", err) + } else { + log.Printf("[INFO] Compacted key database (%+v)", result) + } + + // update main cert + res := &certificate.Resource{} + if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") + } else { + tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) + + // renew main certificate 30 days before it expires + if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { + go (func() { + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + if err != nil { + log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) + } + })() + } + } + + time.Sleep(12 * time.Hour) + } } From e85f21ed2ef460ba61c632f48b6182ab884a38e5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 17:57:54 +0100 Subject: [PATCH 088/320] some renames --- cmd/main.go | 2 +- server/setup.go | 2 +- server/upstream/upstream.go | 71 +++++++++++++++++++------------------ 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index b026885..eafadb6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -80,7 +80,7 @@ func Serve(ctx *cli.Context) error { dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache) fastServer := server.SetupServer(handler) - httpServer := server.SetupHttpACMEChallengeServer(challengeCache) + httpServer := server.SetupHTTPACMEChallengeServer(challengeCache) // Setup listener and TLS log.Info().Msgf("Listening on https://%s", listeningAddress) diff --git a/server/setup.go b/server/setup.go index 546aba1..67c1c42 100644 --- a/server/setup.go +++ b/server/setup.go @@ -27,7 +27,7 @@ func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { } } -func SetupHttpACMEChallengeServer(challengeCache cache.SetGetKey) *fasthttp.Server { +func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) *fasthttp.Server { challengePath := []byte("/.well-known/acme-challenge/") return &fasthttp.Server{ diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 306e1db..2338ef2 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -24,12 +24,13 @@ var upstreamIndexPages = []string{ // Options provides various options for the upstream request. type Options struct { - DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} - TryIndexPages bool - AppendTrailingSlash bool - RedirectIfExists string - BranchTimestamp time.Time + DefaultMimeType string + ForbiddenMimeTypes map[string]struct{} + TryIndexPages bool + BranchTimestamp time.Time + // internal + appendTrailingSlash bool + redirectIfExists string } var client = fasthttp.Client{ @@ -40,23 +41,23 @@ var client = fasthttp.Client{ } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { +func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() - if options.ForbiddenMimeTypes == nil { - options.ForbiddenMimeTypes = map[string]struct{}{} + if o.ForbiddenMimeTypes == nil { + o.ForbiddenMimeTypes = map[string]struct{}{} } // Check if the branch exists and when it was modified - if options.BranchTimestamp == (time.Time{}) { - branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaApiToken, branchTimestampCache) + if o.BranchTimestamp == (time.Time{}) { + branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaAPIToken, branchTimestampCache) if branch == nil { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return true } targetBranch = branch.Branch - options.BranchTimestamp = branch.Timestamp + o.BranchTimestamp = branch.Timestamp } if targetOwner == "" || targetRepo == "" || targetBranch == "" { @@ -66,7 +67,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Check if the browser has a cached version if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { - if !ifModifiedSince.Before(options.BranchTimestamp) { + if !ifModifiedSince.Before(o.BranchTimestamp) { ctx.Response.SetStatusCode(fasthttp.StatusNotModified) return true } @@ -79,11 +80,11 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe var res *fasthttp.Response var cachedResponse fileResponse var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(options.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(o.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaApiToken) + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaAPIToken) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) err = client.Do(req, res) @@ -92,24 +93,24 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Handle errors if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { - if options.TryIndexPages { - // copy the options struct & try if an index page exists - optionsForIndexPages := *options + if o.TryIndexPages { + // copy the o struct & try if an index page exists + optionsForIndexPages := *o optionsForIndexPages.TryIndexPages = false - optionsForIndexPages.AppendTrailingSlash = true + optionsForIndexPages.appendTrailingSlash = true for _, indexPage := range upstreamIndexPages { - if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, fileCacheTimeout) return true } } // compatibility fix for GitHub Pages (/example → /example.html) - optionsForIndexPages.AppendTrailingSlash = false - optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + optionsForIndexPages.appendTrailingSlash = false + optionsForIndexPages.redirectIfExists = string(ctx.Request.URI().Path()) + ".html" + if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, fileCacheTimeout) return true @@ -118,7 +119,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe ctx.Response.SetStatusCode(fasthttp.StatusNotFound) if res != nil { // Update cache if the request is fresh - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, fileCacheTimeout) } @@ -131,8 +132,8 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe } // Append trailing slash if missing (for index files), and redirect to fix filenames in general - // options.AppendTrailingSlash is only true when looking for index pages - if options.AppendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { + // o.appendTrailingSlash is only true when looking for index pages + if o.appendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) return true } @@ -140,8 +141,8 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) return true } - if options.RedirectIfExists != "" { - ctx.Redirect(options.RedirectIfExists, fasthttp.StatusTemporaryRedirect) + if o.redirectIfExists != "" { + ctx.Redirect(o.redirectIfExists, fasthttp.StatusTemporaryRedirect) return true } log.Debug().Msg("error handling") @@ -149,9 +150,9 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Set the MIME type mimeType := mime.TypeByExtension(path.Ext(targetPath)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if _, ok := options.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { - if options.DefaultMimeType != "" { - mimeType = options.DefaultMimeType + if _, ok := o.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { + if o.DefaultMimeType != "" { + mimeType = o.DefaultMimeType } else { mimeType = "application/octet-stream" } @@ -160,7 +161,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe // Everything's okay so far ctx.Response.SetStatusCode(fasthttp.StatusOK) - ctx.Response.Header.SetLastModified(options.BranchTimestamp) + ctx.Response.Header.SetLastModified(o.BranchTimestamp) log.Debug().Msg("response preparations") @@ -187,7 +188,7 @@ func (options *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRe cachedResponse.exists = true cachedResponse.mimeType = mimeType cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), cachedResponse, fileCacheTimeout) + _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), cachedResponse, fileCacheTimeout) } return true From 0374e95d230e39980392006d1647084b09aea99a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 18:17:28 +0100 Subject: [PATCH 089/320] make tryUpstream independent func --- server/handler.go | 57 +++++++++++++++++++++++------------------------ server/try.go | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 server/try.go diff --git a/server/handler.go b/server/handler.go index e7652dd..0aa5f3a 100644 --- a/server/handler.go +++ b/server/handler.go @@ -110,29 +110,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, return true } - // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. - var tryUpstream = func() { - // check if a canonical domain exists on a request on MainDomain - if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) - if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { - canonicalPath := string(ctx.RequestURI()) - if targetRepo != "pages" { - canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] - } - ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) - return - } - } - - // Try to request the file from the Gitea API - if !targetOptions.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaApiToken, branchTimestampCache, fileResponseCache) { - html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) - } - } - log.Debug().Msg("preparations") - if rawDomain != nil && bytes.Equal(trimmedHost, rawDomain) { // Serve raw content from RawDomain log.Debug().Msg("raw domain") @@ -157,7 +135,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) return } log.Debug().Msg("missing branch") @@ -169,7 +150,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -202,7 +186,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, "/"+pathElements[0]+"/%p", ) { log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } @@ -215,7 +202,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with specified branch") if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } @@ -228,7 +218,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -237,7 +230,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with default repo/branch") if tryBranch("pages", "", pathElements, "") { log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -282,7 +278,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, } log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream() + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaApiToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) return } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) diff --git a/server/try.go b/server/try.go new file mode 100644 index 0000000..0636ffc --- /dev/null +++ b/server/try.go @@ -0,0 +1,40 @@ +package server + +import ( + "bytes" + "strings" + + "github.com/valyala/fasthttp" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/upstream" +) + +// tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. +func tryUpstream(ctx *fasthttp.RequestCtx, + mainDomainSuffix, trimmedHost []byte, + + targetOptions *upstream.Options, + targetOwner, targetRepo, targetBranch, targetPath, + + giteaRoot, giteaAPIToken string, + canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) { + // check if a canonical domain exists on a request on MainDomain + if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { + canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) + if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { + canonicalPath := string(ctx.RequestURI()) + if targetRepo != "pages" { + canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + } + ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) + return + } + } + + // Try to request the file from the Gitea API + if !targetOptions.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) + } +} From 26a199053ba41c69f156238dbe984986a68ffcd7 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 18:18:05 +0100 Subject: [PATCH 090/320] lint: rename --- server/handler.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/handler.go b/server/handler.go index 0aa5f3a..1031716 100644 --- a/server/handler.go +++ b/server/handler.go @@ -16,7 +16,7 @@ import ( // Handler handles a single HTTP request to the web server. func Handler(mainDomainSuffix, rawDomain []byte, - giteaRoot, rawInfoPage, giteaApiToken string, + giteaRoot, rawInfoPage, giteaAPIToken string, blacklistedPaths, allowedCorsDomains [][]byte, dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { @@ -85,7 +85,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaApiToken, branchTimestampCache) + branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaAPIToken, branchTimestampCache) if branchTimestampResult == nil { // branch doesn't exist return false @@ -137,7 +137,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -152,7 +152,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -188,7 +188,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -204,7 +204,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -220,7 +220,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -232,7 +232,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -261,7 +261,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) + canonicalDomain, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return @@ -280,7 +280,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("tryBranch, now trying upstream") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaApiToken, + giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } else { From a0534f1fde96b77419311e7c0b9ce237575d54b2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 18:26:54 +0100 Subject: [PATCH 091/320] make MaintainCertDB able to cancel --- cmd/main.go | 8 ++++++-- server/certificates/certificates.go | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index eafadb6..21da71a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,11 +2,13 @@ package cmd import ( "bytes" + "context" "crypto/tls" "errors" "fmt" "net" "strings" + "time" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" @@ -109,8 +111,10 @@ func Serve(ctx *cli.Context) error { certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, keyDatabase) - // TODO: make it graceful - go certificates.MaintainCertDB(mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + interval := 12 * time.Hour + certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) + defer cancelCertMaintain() + go certificates.MaintainCertDB(certMaintainCtx, interval, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) if enableHTTPServer { go func() { diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index e484d78..bb6a3c3 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -2,6 +2,7 @@ package certificates import ( "bytes" + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -446,7 +447,7 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * } } -func MaintainCertDB(mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) { +func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) { for { // clean up expired certs now := time.Now() @@ -503,6 +504,10 @@ func MaintainCertDB(mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimi } } - time.Sleep(12 * time.Hour) + select { + case <-ctx.Done(): + return + case <-time.After(interval): + } } } From 5fe51d86214298637838539230cf43058d041533 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 19:00:57 +0100 Subject: [PATCH 092/320] rm certDB helper and build in --- server/certificates/certificates.go | 33 +++++++++++++++---------- server/certificates/mock.go | 4 +++- server/database/helpers.go | 37 ----------------------------- server/database/interface.go | 9 ++++--- server/database/setup.go | 29 +++++++++++++++++----- 5 files changed, 53 insertions(+), 59 deletions(-) delete mode 100644 server/database/helpers.go diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index bb6a3c3..fa76538 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -188,8 +188,11 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, bool) { // parse certificate from database - res := &certificate.Resource{} - if !database.PogrebGet(keyDatabase, sni, res) { + res, err := keyDatabase.Get(sni) + if err != nil { + panic(err) // TODO: no panic + } + if res == nil { return tls.Certificate{}, false } @@ -294,7 +297,9 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) - database.PogrebPut(keyDatabase, []byte(name), renew) + if err := keyDatabase.Put(name, renew); err != nil { + return mockCert(domains[0], err.Error(), string(mainDomainSuffix), keyDatabase), err + } return tlsCertificate, nil } } @@ -302,7 +307,9 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } log.Printf("Obtained certificate for %v", domains) - database.PogrebPut(keyDatabase, []byte(name), res) + if err := keyDatabase.Put(name, res); err != nil { + return tls.Certificate{}, err + } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { return tls.Certificate{}, err @@ -447,12 +454,12 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * } } -func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) { +func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { for { // clean up expired certs now := time.Now() expiredCertCount := 0 - keyDatabaseIterator := keyDatabase.Items() + keyDatabaseIterator := certDB.Items() key, resBytes, err := keyDatabaseIterator.Next() for err == nil { if !bytes.Equal(key, mainDomainSuffix) { @@ -466,7 +473,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := keyDatabase.Delete(key) + err := certDB.Delete(key) if err != nil { log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) } else { @@ -479,7 +486,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) // compact the database - result, err := keyDatabase.Compact() + result, err := certDB.Compact() if err != nil { log.Printf("[ERROR] Compacting key database failed: %s", err) } else { @@ -487,16 +494,18 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } // update main cert - res := &certificate.Resource{} - if !database.PogrebGet(keyDatabase, mainDomainSuffix, res) { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") + res, err := certDB.Get(mainDomainSuffix) + if err != nil { + log.Err(err).Msgf("could not get cert for domain '%s'", mainDomainSuffix) + } else if res == nil { + log.Error().Msgf("Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) // renew main certificate 30 days before it expires if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) } diff --git a/server/certificates/mock.go b/server/certificates/mock.go index 22d5470..0e87e6e 100644 --- a/server/certificates/mock.go +++ b/server/certificates/mock.go @@ -74,7 +74,9 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) if domain == "*"+mainDomainSuffix || domain == mainDomainSuffix[1:] { databaseName = mainDomainSuffix } - database.PogrebPut(keyDatabase, []byte(databaseName), res) + if err := keyDatabase.Put(databaseName, res); err != nil { + panic(err) + } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { diff --git a/server/database/helpers.go b/server/database/helpers.go deleted file mode 100644 index ea3e899..0000000 --- a/server/database/helpers.go +++ /dev/null @@ -1,37 +0,0 @@ -package database - -import ( - "bytes" - "encoding/gob" -) - -func PogrebPut(db CertDB, name []byte, obj interface{}) { - var resGob bytes.Buffer - resEnc := gob.NewEncoder(&resGob) - err := resEnc.Encode(obj) - if err != nil { - panic(err) - } - err = db.Put(name, resGob.Bytes()) - if err != nil { - panic(err) - } -} - -func PogrebGet(db CertDB, name []byte, obj interface{}) bool { - resBytes, err := db.Get(name) - if err != nil { - panic(err) - } - if resBytes == nil { - return false - } - - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - err = resDec.Decode(obj) - if err != nil { - panic(err) - } - return true -} diff --git a/server/database/interface.go b/server/database/interface.go index 80d74d3..01b9872 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -1,11 +1,14 @@ package database -import "github.com/akrylysov/pogreb" +import ( + "github.com/akrylysov/pogreb" + "github.com/go-acme/lego/v4/certificate" +) type CertDB interface { Close() error - Put(key []byte, value []byte) error - Get(key []byte) ([]byte, error) + Put(name string, cert *certificate.Resource) error + Get(name []byte) (*certificate.Resource, error) Delete(key []byte) error Compact() (pogreb.CompactionResult, error) Items() *pogreb.ItemIterator diff --git a/server/database/setup.go b/server/database/setup.go index f7eeafc..f3cac16 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -1,14 +1,16 @@ package database import ( + "bytes" "context" + "encoding/gob" "fmt" "time" - "github.com/rs/zerolog/log" - "github.com/akrylysov/pogreb" "github.com/akrylysov/pogreb/fs" + "github.com/go-acme/lego/v4/certificate" + "github.com/rs/zerolog/log" ) type aDB struct { @@ -23,12 +25,27 @@ func (p aDB) Close() error { return p.intern.Sync() } -func (p aDB) Put(key []byte, value []byte) error { - return p.intern.Put(key, value) +func (p aDB) Put(name string, cert *certificate.Resource) error { + var resGob bytes.Buffer + if err := gob.NewEncoder(&resGob).Encode(cert); err != nil { + return err + } + return p.intern.Put([]byte(name), resGob.Bytes()) } -func (p aDB) Get(key []byte) ([]byte, error) { - return p.intern.Get(key) +func (p aDB) Get(name []byte) (*certificate.Resource, error) { + cert := &certificate.Resource{} + resBytes, err := p.intern.Get(name) + if err != nil { + return nil, err + } + if resBytes == nil { + return nil, nil + } + if err = gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil { + return nil, err + } + return cert, nil } func (p aDB) Delete(key []byte) error { From 2f6b280fcebdea91c5e662c31f5c6400c5d772a7 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 19:02:26 +0100 Subject: [PATCH 093/320] meaningfull var names --- cmd/main.go | 10 +++++----- server/certificates/certificates.go | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 21da71a..a1bee6a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -92,29 +92,29 @@ func Serve(ctx *cli.Context) error { } // TODO: make "key-database.pogreb" set via flag - keyDatabase, err := database.New("key-database.pogreb") + certDB, err := database.New("key-database.pogreb") if err != nil { return fmt.Errorf("could not create database: %v", err) } - defer keyDatabase.Close() //nolint:errcheck // database has no close ... sync behave like it + defer certDB.Close() //nolint:errcheck // database has no close ... sync behave like it listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaRoot, giteaAPIToken, dnsProvider, acmeUseRateLimits, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, - keyDatabase)) + certDB)) acmeConfig, err := certificates.SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeAcceptTerms) if err != nil { return err } - certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, keyDatabase) + certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, certDB) interval := 12 * time.Hour certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) defer cancelCertMaintain() - go certificates.MaintainCertDB(certMaintainCtx, interval, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + go certificates.MaintainCertDB(certMaintainCtx, interval, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB) if enableHTTPServer { go func() { diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index fa76538..105f049 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -36,10 +36,10 @@ import ( // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. func TLSConfig(mainDomainSuffix []byte, - giteaRoot, giteaApiToken, dnsProvider string, + giteaRoot, giteaAPIToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, - keyDatabase database.CertDB) *tls.Config { + certDB database.CertDB) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -79,7 +79,7 @@ func TLSConfig(mainDomainSuffix []byte, sni = string(sniBytes) } else { _, _ = targetRepo, targetBranch - _, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaApiToken, canonicalDomainCache) + _, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) if !valid { sniBytes = mainDomainSuffix sni = string(sniBytes) @@ -95,13 +95,13 @@ func TLSConfig(mainDomainSuffix []byte, var tlsCertificate tls.Certificate var err error var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase); !ok { + if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); !ok { // request a new certificate if bytes.Equal(sniBytes, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { return nil, err } @@ -186,9 +186,9 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { // parse certificate from database - res, err := keyDatabase.Get(sni) + res, err := certDB.Get(sni) if err != nil { panic(err) // TODO: no panic } @@ -220,7 +220,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs } go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { log.Printf("Couldn't renew certificate for %s: %s", sni, err) } @@ -400,9 +400,9 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce return myAcmeConfig, nil } -func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, keyDatabase database.CertDB) { +func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) { // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits - mainCertBytes, err := keyDatabase.Get(mainDomainSuffix) + mainCertBytes, err := certDB.Get(mainDomainSuffix) if err != nil { // key database is not working panic(err) @@ -447,7 +447,7 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * } if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, keyDatabase) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } From a7bb3448a41006c3e8dbc8ed16622dca4284095d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 19:53:23 +0100 Subject: [PATCH 094/320] move more args of Upstream() to upstream Options --- server/handler.go | 36 ++++++++++++++++++------------------ server/try.go | 8 +++++++- server/upstream/upstream.go | 29 ++++++++++++++++++----------- server/utils/utils_test.go | 13 +++++++++++++ 4 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 server/utils/utils_test.go diff --git a/server/handler.go b/server/handler.go index 1031716..1aaf476 100644 --- a/server/handler.go +++ b/server/handler.go @@ -144,19 +144,19 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("missing branch") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return - } else { - log.Debug().Msg("raw domain preparations, now trying with default branch") - tryBranch(targetRepo, "", pathElements[2:], - giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", - ) - log.Debug().Msg("tryBranch, now trying upstream") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, - canonicalDomainCache, branchTimestampCache, fileResponseCache) - return } + log.Debug().Msg("raw domain preparations, now trying with default branch") + tryBranch(targetRepo, "", pathElements[2:], + giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", + ) + log.Debug().Msg("tryBranch, now trying upstream") + tryUpstream(ctx, mainDomainSuffix, trimmedHost, + targetOptions, targetOwner, targetRepo, targetBranch, targetPath, + giteaRoot, giteaAPIToken, + canonicalDomainCache, branchTimestampCache, fileResponseCache) + return + } else if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix log.Debug().Msg("main domain suffix") @@ -167,7 +167,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") if targetOwner == "www" { - // www.codeberg.page redirects to codeberg.page + // www.codeberg.page redirects to codeberg.page // TODO: rm hardcoded - use cname? ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), fasthttp.StatusPermanentRedirect) return } @@ -271,10 +271,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) return - } else { - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) - return } + + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + return } log.Debug().Msg("tryBranch, now trying upstream") @@ -283,10 +283,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return - } else { - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) - return } + + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + return } } } diff --git a/server/try.go b/server/try.go index 0636ffc..7223dfa 100644 --- a/server/try.go +++ b/server/try.go @@ -20,6 +20,7 @@ func tryUpstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken string, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) { + // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) @@ -33,8 +34,13 @@ func tryUpstream(ctx *fasthttp.RequestCtx, } } + targetOptions.TargetOwner = targetOwner + targetOptions.TargetRepo = targetRepo + targetOptions.TargetBranch = targetBranch + targetOptions.TargetPath = targetPath + // Try to request the file from the Gitea API - if !targetOptions.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + if !targetOptions.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) } } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 2338ef2..925183a 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -24,7 +24,12 @@ var upstreamIndexPages = []string{ // Options provides various options for the upstream request. type Options struct { - DefaultMimeType string + TargetOwner, + TargetRepo, + TargetBranch, + TargetPath, + + DefaultMimeType string ForbiddenMimeTypes map[string]struct{} TryIndexPages bool BranchTimestamp time.Time @@ -41,26 +46,26 @@ var client = fasthttp.Client{ } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { - log := log.With().Strs("upstream", []string{targetOwner, targetRepo, targetBranch, targetPath}).Logger() +func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { + log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() if o.ForbiddenMimeTypes == nil { o.ForbiddenMimeTypes = map[string]struct{}{} } // Check if the branch exists and when it was modified - if o.BranchTimestamp == (time.Time{}) { - branch := GetBranchTimestamp(targetOwner, targetRepo, targetBranch, giteaRoot, giteaAPIToken, branchTimestampCache) + if o.BranchTimestamp.IsZero() { + branch := GetBranchTimestamp(o.TargetOwner, o.TargetRepo, o.TargetBranch, giteaRoot, giteaAPIToken, branchTimestampCache) if branch == nil { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return true } - targetBranch = branch.Branch + o.TargetBranch = branch.Branch o.BranchTimestamp = branch.Timestamp } - if targetOwner == "" || targetRepo == "" || targetBranch == "" { + if o.TargetOwner == "" || o.TargetRepo == "" || o.TargetBranch == "" { html.ReturnErrorPage(ctx, fasthttp.StatusBadRequest) return true } @@ -75,7 +80,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, ta log.Debug().Msg("preparations") // Make a GET request to the upstream URL - uri := targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/" + targetPath + uri := o.TargetOwner + "/" + o.TargetRepo + "/raw/" + o.TargetBranch + "/" + o.TargetPath var req *fasthttp.Request var res *fasthttp.Response var cachedResponse fileResponse @@ -99,7 +104,8 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, ta optionsForIndexPages.TryIndexPages = false optionsForIndexPages.appendTrailingSlash = true for _, indexPage := range upstreamIndexPages { - if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, strings.TrimSuffix(targetPath, "/")+"/"+indexPage, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + optionsForIndexPages.TargetPath = strings.TrimSuffix(o.TargetPath, "/") + "/" + indexPage + if optionsForIndexPages.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, fileCacheTimeout) @@ -109,7 +115,8 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, ta // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.appendTrailingSlash = false optionsForIndexPages.redirectIfExists = string(ctx.Request.URI().Path()) + ".html" - if optionsForIndexPages.Upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath+".html", giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + optionsForIndexPages.TargetPath = o.TargetPath + ".html" + if optionsForIndexPages.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, }, fileCacheTimeout) @@ -148,7 +155,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, ta log.Debug().Msg("error handling") // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(targetPath)) + mimeType := mime.TypeByExtension(path.Ext(o.TargetPath)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) if _, ok := o.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { if o.DefaultMimeType != "" { diff --git a/server/utils/utils_test.go b/server/utils/utils_test.go new file mode 100644 index 0000000..3dc0632 --- /dev/null +++ b/server/utils/utils_test.go @@ -0,0 +1,13 @@ +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTrimHostPort(t *testing.T) { + assert.EqualValues(t, "aa", TrimHostPort([]byte("aa"))) + assert.EqualValues(t, "", TrimHostPort([]byte(":"))) + assert.EqualValues(t, "example.com", TrimHostPort([]byte("example.com:80"))) +} From 67a190f68ae2d351b24a2f9518ba31d01898f7cf Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 5 Dec 2021 22:12:48 +0100 Subject: [PATCH 095/320] Hotfix for #27: avoid slash before .html in GitHub compatibility redirects --- handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler.go b/handler.go index 626385d..d5b0448 100644 --- a/handler.go +++ b/handler.go @@ -446,7 +446,7 @@ func upstream(ctx *fasthttp.RequestCtx, targetOwner string, targetRepo string, t } // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.AppendTrailingSlash = false - optionsForIndexPages.RedirectIfExists = string(ctx.Request.URI().Path()) + ".html" + optionsForIndexPages.RedirectIfExists = strings.TrimSuffix(string(ctx.Request.URI().Path()), "/") + ".html" if upstream(ctx, targetOwner, targetRepo, targetBranch, targetPath + ".html", &optionsForIndexPages) { _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(options.BranchTimestamp.Unix(), 10), fileResponse{ exists: false, From 196482da07af0a12f788a48f2d5468954bf9f541 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 23:20:34 +0100 Subject: [PATCH 096/320] less panic --- go.mod | 1 + server/certificates/certificates.go | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index a2bd8ee..615bb12 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-acme/lego/v4 v4.5.3 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.26.0 + github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 105f049..b40c76d 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -11,6 +11,7 @@ import ( "encoding/gob" "encoding/json" "errors" + "fmt" "io/ioutil" "os" "strconv" @@ -107,9 +108,8 @@ func TLSConfig(mainDomainSuffix []byte, } } - err = keyCache.Set(sni, &tlsCertificate, 15*time.Minute) - if err != nil { - panic(err) + if err := keyCache.Set(sni, &tlsCertificate, 15*time.Minute); err != nil { + return nil, err } return &tlsCertificate, nil }, @@ -323,13 +323,12 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce var myAcmeConfig *lego.Config if account, err := ioutil.ReadFile(configFile); err == nil { - err = json.Unmarshal(account, &myAcmeAccount) - if err != nil { - panic(err) + if err := json.Unmarshal(account, &myAcmeAccount); err != nil { + return nil, err } myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM)) if err != nil { - panic(err) + return nil, err } myAcmeConfig = lego.NewConfig(&myAcmeAccount) myAcmeConfig.CADirURL = acmeAPI @@ -348,7 +347,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - panic(err) + return nil, err } myAcmeAccount = AcmeAccount{ Email: acmeMail, @@ -384,12 +383,12 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce } if myAcmeAccount.Registration != nil { - acmeAccountJson, err := json.Marshal(myAcmeAccount) + acmeAccountJSON, err := json.Marshal(myAcmeAccount) if err != nil { log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) select {} } - err = ioutil.WriteFile(configFile, acmeAccountJson, 0600) + err = ioutil.WriteFile(configFile, acmeAccountJSON, 0600) if err != nil { log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) select {} @@ -400,12 +399,11 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce return myAcmeConfig, nil } -func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) { - // getting main cert before ACME account so that we can panic here on database failure without hitting rate limits +func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { + // getting main cert before ACME account so that we can fail here without hitting rate limits mainCertBytes, err := certDB.Get(mainDomainSuffix) if err != nil { - // key database is not working - panic(err) + return fmt.Errorf("cert database is not working") } acmeClient, err = lego.NewClient(acmeConfig) @@ -452,6 +450,8 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) } } + + return nil } func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { From aa0638903a083e7e037b91831795369350002dc6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 5 Dec 2021 23:56:06 +0100 Subject: [PATCH 097/320] fix argument check and some nits --- cmd/main.go | 6 ++++-- html/error.go | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index a1bee6a..cd1a1c2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -49,7 +49,7 @@ func Serve(ctx *cli.Context) error { acmeEabKID := ctx.String("acme-eab-kid") acmeEabHmac := ctx.String("acme-eab-hmac") dnsProvider := ctx.String("dns-provider") - if acmeAcceptTerms || (dnsProvider == "" && acmeAPI != "https://acme.mock.directory") { + if (!acmeAcceptTerms || dnsProvider == "") && acmeAPI != "https://acme.mock.directory" { return errors.New("you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory") } @@ -109,7 +109,9 @@ func Serve(ctx *cli.Context) error { return err } - certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, certDB) + if err := certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, certDB); err != nil { + return err + } interval := 12 * time.Hour certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) diff --git a/html/error.go b/html/error.go index f831443..325dada 100644 --- a/html/error.go +++ b/html/error.go @@ -2,8 +2,9 @@ package html import ( "bytes" - "github.com/valyala/fasthttp" "strconv" + + "github.com/valyala/fasthttp" ) // ReturnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced From 70c7065f764231c40f266ff39e412f15124b9cfa Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 9 Dec 2021 19:32:30 +0100 Subject: [PATCH 098/320] fix #31 --- server/try.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/try.go b/server/try.go index 7223dfa..6dffa44 100644 --- a/server/try.go +++ b/server/try.go @@ -27,7 +27,10 @@ func tryUpstream(ctx *fasthttp.RequestCtx, if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { - canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + path := strings.SplitN(canonicalPath, "/", 3) + if len(path) >= 3 { + canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + } } ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) return From 6af6523a0f83a171e19b5bb9c2b25b28d4916bee Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 9 Dec 2021 20:16:43 +0100 Subject: [PATCH 099/320] code format --- server/try.go | 2 +- server/upstream/domains.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/try.go b/server/try.go index 6dffa44..31cd7f4 100644 --- a/server/try.go +++ b/server/try.go @@ -29,7 +29,7 @@ func tryUpstream(ctx *fasthttp.RequestCtx, if targetRepo != "pages" { path := strings.SplitN(canonicalPath, "/", 3) if len(path) >= 3 { - canonicalPath = "/" + strings.SplitN(canonicalPath, "/", 3)[2] + canonicalPath = "/" + path[2] } } ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 5971e13..47a5564 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -8,9 +8,10 @@ import ( "codeberg.org/codeberg/pages/server/cache" ) -// CheckCanonicalDomain returns the canonical domain specified in the repo (using the file `.canonical-domain`). -func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaApiToken string, canonicalDomainCache cache.SetGetKey) (canonicalDomain string, valid bool) { +// CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). +func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken string, canonicalDomainCache cache.SetGetKey) (string, bool) { domains := []string{} + valid := false if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) for _, domain := range domains { @@ -21,7 +22,7 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } } else { req := fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaApiToken) + req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaAPIToken) res := fasthttp.AcquireResponse() err := client.Do(req, res) @@ -48,6 +49,5 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, canonicalDomainCacheTimeout) } - canonicalDomain = domains[0] - return + return domains[0], valid } From 73fa2da6467e196d9afbec1487401e16f4887842 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 10 Dec 2021 14:31:58 +0100 Subject: [PATCH 100/320] Update default to raw.codeberg.page & improve documentation on custom domains --- README.md | 9 ++++++--- cmd/flags.go | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 616d69d..087d7ae 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ - `HOST` & `PORT` (default: `[::]` & `443`): listen address. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. -- `RAW_DOMAIN` (default: `raw.codeberg.org`): domain for raw resources. +- `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources. - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. - `RAW_INFO_PAGE` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. @@ -15,6 +15,7 @@ - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. +- `DEBUG` (default: false): Set this to true to enable debug logging. ``` // Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. @@ -29,8 +30,10 @@ // www.example.org. IN CNAME main.pages.example.codeberg.page. // // 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record -// for "example.org" (if your provider allows ALIAS or similar records): +// for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT +// record that points to your repo (just like the CNAME record): // example.org IN ALIAS codeberg.page. +// example.org IN TXT main.pages.example.codeberg.page. // // Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. -``` \ No newline at end of file +``` diff --git a/cmd/flags.go b/cmd/flags.go index 6381ee5..c6eaf4f 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -35,14 +35,14 @@ var ServeFlags = []cli.Flag{ Name: "raw-domain", Usage: "specifies the domain from which raw repository content shall be served, not set disable raw content hosting", EnvVars: []string{"RAW_DOMAIN"}, - Value: "raw.codeberg.org", + Value: "raw.codeberg.page", }, // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). &cli.StringFlag{ Name: "raw-info-page", Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", EnvVars: []string{"RAW_INFO_PAGE"}, - Value: "https://docs.codeberg.org/pages/raw-content/", + Value: "https://docs.codeberg.org/codeberg-pages/raw-content/", }, // Server From adfc96ab94d3e7a9b8210220ee934c4fb24bfd12 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Fri, 10 Dec 2021 14:32:14 +0100 Subject: [PATCH 101/320] Add --verbose flag and hide debug messages by default --- Justfile | 2 +- cmd/flags.go | 6 ++++++ cmd/main.go | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index a5feb7a..3d23175 100644 --- a/Justfile +++ b/Justfile @@ -6,7 +6,7 @@ dev: export PAGES_DOMAIN=localhost.mock.directory export RAW_DOMAIN=raw.localhost.mock.directory export PORT=4430 - go run . + go run . --verbose build: CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ diff --git a/cmd/flags.go b/cmd/flags.go index c6eaf4f..e1838c2 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -5,6 +5,12 @@ import ( ) var ServeFlags = []cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + // TODO: Usage + EnvVars: []string{"DEBUG"}, + }, + // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". diff --git a/cmd/main.go b/cmd/main.go index cd1a1c2..fb0c26e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" @@ -34,6 +35,11 @@ var BlacklistedPaths = [][]byte{ // Serve sets up and starts the web server. func Serve(ctx *cli.Context) error { + verbose := ctx.Bool("verbose") + if !verbose { + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } + giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") giteaAPIToken := ctx.String("gitea-api-token") rawDomain := ctx.String("raw-domain") From 4404287958ad9b9e1c4311a8d5a72583aa75f77a Mon Sep 17 00:00:00 2001 From: fnetx Date: Fri, 11 Feb 2022 01:31:11 +0100 Subject: [PATCH 102/320] Update 404 Not found page --- html/404.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/html/404.html b/html/404.html index fa17a83..08ab16d 100644 --- a/html/404.html +++ b/html/404.html @@ -21,12 +21,13 @@ - +

- You found a bug! + This page was not found!

- Sorry, this page doesn't exist or is inaccessible for other reasons (%status) + Sorry, this page doesn't exist or is inaccessible for other reasons (%status).
+ We hope this is not our fault ;) - Make sure to check the troubleshooting section in the Docs!
From ac5b19123d873612b7764add6da8632bed555d97 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sat, 19 Feb 2022 18:10:40 +0100 Subject: [PATCH 103/320] Update README (#57) I hope this makes it more inviting to collaborate with us on this project. I'd like to promote the software a little more. Co-authored-by: fnetx Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/57 Co-authored-by: Otto Richter Co-committed-by: Otto Richter --- README.md | 102 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 087d7ae..b56239b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,58 @@ -## Environment +# Codeberg Pages + +Gitea lacks the ability to host static pages from Git. +The Codeberg Pages Server addresses this lack by implementing a standalone service +that connects to Gitea via API. +It is suitable to be deployed by other Gitea instances, too, to offer static pages hosting to their users. + +**End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview) +and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/). + + +## Quickstart + +This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. +Mapping custom domains is not static anymore, but can be done with DNS: + +1) add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The +first line will be the canonical domain/URL; all other occurrences will be redirected to it. + +2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to +"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): + `www.example.org. IN CNAME main.pages.example.codeberg.page.` + +3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record +for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT +record that points to your repo (just like the CNAME record): + `example.org IN ALIAS codeberg.page.` + `example.org IN TXT main.pages.example.codeberg.page.` + +Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. + + +## Deployment + +**Warning: Some Caveats Apply** +> Currently, the deployment requires you to have some knowledge of system administration as well as understanding and building code, +> so you can eventually edit non-configurable and codeberg-specific settings. +> In the future, we'll try to reduce these and make hosting Codeberg Pages as easy as setting up Gitea. +> If you consider using Pages in practice, please consider contacting us first, +> we'll then try to share some basic steps and document the current usage for admins +> (might be changing in the current state). + +Deploying the software itself is very easy. You can grab a current release binary or build yourself, +configure the environment as described below, and you are done. + +The hard part is about adding **custom domain support** if you intend to use it. +SSL certificates (request + renewal) is automatically handled by the Pages Server, +but if you want to run it on a shared IP address (and not a standalone), +you'll need to configure your reverse proxy not to terminate the TLS connections, +but forward the requests on the IP level to the Pages Server. + +You can check out a proof of concept in the `haproxy-sni` folder, +and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/haproxy-sni/haproxy.cfg#L38). + +### Environment - `HOST` & `PORT` (default: `[::]` & `443`): listen address. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. @@ -17,23 +71,29 @@ See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. - `DEBUG` (default: false): Set this to true to enable debug logging. -``` -// Package main is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. -// -// Mapping custom domains is not static anymore, but can be done with DNS: -// -// 1) add a ".domains" text file to your repository, containing the allowed domains, separated by new lines. The -// first line will be the canonical domain/URL; all other occurrences will be redirected to it. -// -// 2) add a CNAME entry to your domain, pointing to "[[{branch}.]{repo}.]{owner}.codeberg.page" (repo defaults to -// "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): -// www.example.org. IN CNAME main.pages.example.codeberg.page. -// -// 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record -// for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT -// record that points to your repo (just like the CNAME record): -// example.org IN ALIAS codeberg.page. -// example.org IN TXT main.pages.example.codeberg.page. -// -// Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. -``` + +## Contributing to the development + +The Codeberg team is very open to your contribution. +Since we are working nicely in a team, it might be hard at times to get started +(still check out the issues, we always aim to have some things to get you started). + +If you have any questions, want to work on a feature or could imagine collaborating with us for some time, +feel free to ping us in an issue or in a general Matrix chatgroup. + +You can also contact the maintainers of this project: + +- [momar](https://codeberg.org/momar) [(Matrix)](https://matrix.to/#/@moritz:wuks.space) +- [6543](https://codeberg.org/6543) [(Matrix)](https://matrix.to/#/@marddl:obermui.de) + +### First steps + +The code of this repository is split in several modules. +While heavy refactoring work is currently undergo, you can easily understand the basic structure: +The `cmd` folder holds the data necessary for interacting with the service via the cli. +If you are considering to deploy the service yourself, make sure to check it out. +The heart of the software lives in the `server` folder and is split in several modules. +After scanning the code, you should quickly be able to understand their function and start hacking on them. + +Again: Feel free to get in touch with us for any questions that might arise. +Thank you very much. From cf9e6d9dc635dd77a2d48cd38e925347c4a60bd3 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Mon, 28 Feb 2022 21:50:13 +0100 Subject: [PATCH 104/320] Fix certs only being renewed 7 or 30 days *after* they expire instead of before Seems like plus, minus, greater than and less than are the most complex to understand mathematical concepts... --- server/certificates/certificates.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index b40c76d..505212f 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -209,7 +209,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs } // renew certificates 7 days before they expire - if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(-7 * 24 * time.Hour)) { + if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(7 * 24 * time.Hour)) { // TODO: add ValidUntil to custom res struct if res.CSR != nil && len(res.CSR) > 0 { // CSR stores the time when the renewal shall be tried again @@ -503,7 +503,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) // renew main certificate 30 days before it expires - if !tlsCertificates[0].NotAfter.After(time.Now().Add(-30 * 24 * time.Hour)) { + if !tlsCertificates[0].NotAfter.After(time.Now().Add(30 * 24 * time.Hour)) { go (func() { _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { From a5504acb0e036b67bf8880fc70feff9adb63681e Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 20 Mar 2022 23:18:00 +0100 Subject: [PATCH 105/320] Fix cert removal command (#50) The command was using parts from the old os.Args approach and parts from the cli package, and together they didn't work at all. This fixes that and makes the command `pages-server certs remove [domain...]`. Co-authored-by: Moritz Marquardt Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/50 Co-authored-by: Moritz Marquardt Co-committed-by: Moritz Marquardt --- cmd/certs.go | 77 +++++++++++++++++++++++++++++++++++----------------- cmd/flags.go | 2 +- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/cmd/certs.go b/cmd/certs.go index 6603e55..071234b 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -2,43 +2,70 @@ package cmd import ( "fmt" - "os" - + "github.com/akrylysov/pogreb" "github.com/urfave/cli/v2" "codeberg.org/codeberg/pages/server/database" ) var Certs = &cli.Command{ - Name: "certs", - Usage: "manage certs manually", - Action: certs, + Name: "certs", + Usage: "manage certs manually", + Subcommands: []*cli.Command{ + &cli.Command{ + Name: "list", + Usage: "list all certificates in the database", + Action: listCerts, + }, + &cli.Command{ + Name: "remove", + Usage: "remove a certificate from the database", + Action: removeCert, + }, + }, } -func certs(ctx *cli.Context) error { - if ctx.Args().Len() >= 1 && ctx.Args().First() == "--remove-certificate" { - if ctx.Args().Len() == 1 { - println("--remove-certificate requires at least one domain as an argument") - os.Exit(1) - } +func listCerts(ctx *cli.Context) error { + // TODO: make "key-database.pogreb" set via flag + keyDatabase, err := database.New("key-database.pogreb") + if err != nil { + return fmt.Errorf("could not create database: %v", err) + } - domains := ctx.Args().Slice()[2:] - - // TODO: make "key-database.pogreb" set via flag - keyDatabase, err := database.New("key-database.pogreb") + items := keyDatabase.Items() + for domain, _, err := items.Next(); err != pogreb.ErrIterationDone; domain, _, err = items.Next() { if err != nil { - return fmt.Errorf("could not create database: %v", err) + return err } - - for _, domain := range domains { - if err := keyDatabase.Delete([]byte(domain)); err != nil { - panic(err) - } + if domain[0] == '.' { + fmt.Printf("*") } - if err := keyDatabase.Close(); err != nil { - panic(err) - } - os.Exit(0) + fmt.Printf("%s\n", domain) + } + return nil +} + +func removeCert(ctx *cli.Context) error { + if ctx.Args().Len() < 1 { + return fmt.Errorf("'certs remove' requires at least one domain as an argument") + } + + domains := ctx.Args().Slice() + + // TODO: make "key-database.pogreb" set via flag + keyDatabase, err := database.New("key-database.pogreb") + if err != nil { + return fmt.Errorf("could not create database: %v", err) + } + + for _, domain := range domains { + fmt.Printf("Removing domain %s from the database...\n", domain) + if err := keyDatabase.Delete([]byte(domain)); err != nil { + return err + } + } + if err := keyDatabase.Close(); err != nil { + return err } return nil } diff --git a/cmd/flags.go b/cmd/flags.go index e1838c2..78040be 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -10,7 +10,7 @@ var ServeFlags = []cli.Flag{ // TODO: Usage EnvVars: []string{"DEBUG"}, }, - + // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". From f5d0dc7447c5dd5a079b518a457d7559015a3fce Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Sun, 27 Mar 2022 21:54:06 +0200 Subject: [PATCH 106/320] Add pipeline (#65) close #54 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/65 Reviewed-by: Andreas Shimokawa --- .gitignore | 1 + .woodpecker.yml | 25 +++++++++++++++++++++++++ cmd/certs.go | 5 +++-- cmd/main.go | 8 ++++---- main.go | 6 ++---- server/certificates/acme_account.go | 2 ++ server/certificates/certificates.go | 13 +++++++++---- server/database/setup.go | 16 ++-------------- server/handler.go | 7 ++++--- server/handler_test.go | 3 ++- server/try.go | 4 ++-- server/upstream/helper.go | 4 ++-- 12 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 .woodpecker.yml diff --git a/.gitignore b/.gitignore index 3117f77..dfe69ac 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ key-database.pogreb/ acme-account.json build/ vendor/ +pages diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..eafb569 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,25 @@ +pipeline: + # use vendor to cache dependencies + vendor: + image: golang + commands: + - go mod vendor + + lint: + image: golangci/golangci-lint:v1.45.2 + commands: + - go version + - go install mvdan.cc/gofumpt@latest + - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" + - golangci-lint run + + # # TODO: make tests work + # test: + # image: golang + # commands: + # - go test ./... + + build: + image: golang + commands: + - go build diff --git a/cmd/certs.go b/cmd/certs.go index 071234b..83f2ac5 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/akrylysov/pogreb" "github.com/urfave/cli/v2" @@ -12,12 +13,12 @@ var Certs = &cli.Command{ Name: "certs", Usage: "manage certs manually", Subcommands: []*cli.Command{ - &cli.Command{ + { Name: "list", Usage: "list all certificates in the database", Action: listCerts, }, - &cli.Command{ + { Name: "remove", Usage: "remove a certificate from the database", Action: removeCert, diff --git a/cmd/main.go b/cmd/main.go index fb0c26e..6836cee 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -72,14 +72,14 @@ func Serve(ctx *cli.Context) error { keyCache := cache.NewKeyValueCache() challengeCache := cache.NewKeyValueCache() // canonicalDomainCache stores canonical domains - var canonicalDomainCache = cache.NewKeyValueCache() + canonicalDomainCache := cache.NewKeyValueCache() // dnsLookupCache stores DNS lookups for custom domains - var dnsLookupCache = cache.NewKeyValueCache() + dnsLookupCache := cache.NewKeyValueCache() // branchTimestampCache stores branch timestamps for faster cache checking - var branchTimestampCache = cache.NewKeyValueCache() + branchTimestampCache := cache.NewKeyValueCache() // fileResponseCache stores responses from the Gitea server // TODO: make this an MRU cache with a size limit - var fileResponseCache = cache.NewKeyValueCache() + fileResponseCache := cache.NewKeyValueCache() // Create handler based on settings handler := server.Handler(mainDomainSuffix, []byte(rawDomain), diff --git a/main.go b/main.go index 41aba22..205ec93 100644 --- a/main.go +++ b/main.go @@ -9,10 +9,8 @@ import ( "codeberg.org/codeberg/pages/cmd" ) -var ( - // can be changed with -X on compile - version = "dev" -) +// can be changed with -X on compile +var version = "dev" func main() { app := cli.NewApp() diff --git a/server/certificates/acme_account.go b/server/certificates/acme_account.go index 2ee2e80..57f4d44 100644 --- a/server/certificates/acme_account.go +++ b/server/certificates/acme_account.go @@ -19,9 +19,11 @@ var _ registration.User = &AcmeAccount{} func (u *AcmeAccount) GetEmail() string { return u.Email } + func (u AcmeAccount) GetRegistration() *registration.Resource { return u.Registration } + func (u *AcmeAccount) GetPrivateKey() crypto.PrivateKey { return u.Key } diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 505212f..a13215c 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -40,7 +40,8 @@ func TLSConfig(mainDomainSuffix []byte, giteaRoot, giteaAPIToken, dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, - certDB database.CertDB) *tls.Config { + certDB database.CertDB, +) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -146,8 +147,10 @@ func checkUserLimit(user string) error { return nil } -var acmeClient, mainDomainAcmeClient *lego.Client -var acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} +var ( + acmeClient, mainDomainAcmeClient *lego.Client + acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} +) // rate limit is 300 / 3 hours, we want 200 / 2 hours but to refill more often, so that's 25 new domains every 15 minutes // TODO: when this is used a lot, we probably have to think of a somewhat better solution? @@ -166,6 +169,7 @@ var _ challenge.Provider = AcmeTLSChallengeProvider{} func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { return a.challengeCache.Set(domain, keyAuth, 1*time.Hour) } + func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { a.challengeCache.Remove(domain) return nil @@ -181,6 +185,7 @@ var _ challenge.Provider = AcmeHTTPChallengeProvider{} func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { return a.challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) } + func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { a.challengeCache.Remove(domain + "/" + token) return nil @@ -388,7 +393,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) select {} } - err = ioutil.WriteFile(configFile, acmeAccountJSON, 0600) + err = ioutil.WriteFile(configFile, acmeAccountJSON, 0o600) if err != nil { log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) select {} diff --git a/server/database/setup.go b/server/database/setup.go index f3cac16..e48b661 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -13,6 +13,8 @@ import ( "github.com/rs/zerolog/log" ) +var _ CertDB = aDB{} + type aDB struct { ctx context.Context cancel context.CancelFunc @@ -76,20 +78,6 @@ func (p aDB) sync() { } } -func (p aDB) compact() { - for { - err := p.intern.Sync() - if err != nil { - log.Err(err).Msg("Syncing cert database failed") - } - select { - case <-p.ctx.Done(): - return - case <-time.After(p.syncInterval): - } - } -} - func New(path string) (CertDB, error) { if path == "" { return nil, fmt.Errorf("path not set") diff --git a/server/handler.go b/server/handler.go index 1aaf476..60f5809 100644 --- a/server/handler.go +++ b/server/handler.go @@ -18,7 +18,8 @@ import ( func Handler(mainDomainSuffix, rawDomain []byte, giteaRoot, rawInfoPage, giteaAPIToken string, blacklistedPaths, allowedCorsDomains [][]byte, - dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) func(ctx *fasthttp.RequestCtx) { + dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, +) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() @@ -72,14 +73,14 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Prepare request information to Gitea var targetOwner, targetRepo, targetBranch, targetPath string - var targetOptions = &upstream.Options{ + targetOptions := &upstream.Options{ ForbiddenMimeTypes: map[string]struct{}{}, TryIndexPages: true, } // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will // also disallow search indexing and add a Link header to the canonical URL. - var tryBranch = func(repo string, branch string, path []string, canonicalLink string) bool { + tryBranch := func(repo, branch string, path []string, canonicalLink string) bool { if repo == "" { return false } diff --git a/server/handler_test.go b/server/handler_test.go index 0ec9fcd..34aed7f 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -2,10 +2,11 @@ package server import ( "fmt" - "github.com/valyala/fasthttp" "testing" "time" + "github.com/valyala/fasthttp" + "codeberg.org/codeberg/pages/server/cache" ) diff --git a/server/try.go b/server/try.go index 31cd7f4..4eda5b2 100644 --- a/server/try.go +++ b/server/try.go @@ -19,8 +19,8 @@ func tryUpstream(ctx *fasthttp.RequestCtx, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken string, - canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey) { - + canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, +) { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) diff --git a/server/upstream/helper.go b/server/upstream/helper.go index b5ee77a..f61d746 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -27,7 +27,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, br result.Branch = branch if branch == "" { // Get default branch - var body = make([]byte, 0) + body := make([]byte, 0) // TODO: use header for API key? status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) if err != nil || status != 200 { @@ -37,7 +37,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, br result.Branch = fastjson.GetString(body, "default_branch") } - var body = make([]byte, 0) + body := make([]byte, 0) status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) if err != nil || status != 200 { return nil From 1e4dfe2ae8b06d8a0e712d83672e5210361ecfdd Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Wed, 30 Mar 2022 21:31:09 +0200 Subject: [PATCH 107/320] Fix tests to let CI pass (#66) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/66 Reviewed-by: Otto Richter --- .woodpecker.yml | 9 +++---- server/handler_test.go | 60 ++++++++++++++---------------------------- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index eafb569..76d9f3a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -13,11 +13,10 @@ pipeline: - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - golangci-lint run - # # TODO: make tests work - # test: - # image: golang - # commands: - # - go test ./... + test: + image: golang + commands: + - go test ./... build: image: golang diff --git a/server/handler_test.go b/server/handler_test.go index 34aed7f..3b4d21a 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -25,46 +25,26 @@ func TestHandlerPerformance(t *testing.T) { cache.NewKeyValueCache(), ) - ctx := &fasthttp.RequestCtx{ - Request: *fasthttp.AcquireRequest(), - Response: *fasthttp.AcquireResponse(), - } - ctx.Request.SetRequestURI("http://mondstern.codeberg.page/") - fmt.Printf("Start: %v\n", time.Now()) - start := time.Now() - testHandler(ctx) - end := time.Now() - fmt.Printf("Done: %v\n", time.Now()) - if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { - t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) - } else { - t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + testCase := func(uri string, status int) { + ctx := &fasthttp.RequestCtx{ + Request: *fasthttp.AcquireRequest(), + Response: *fasthttp.AcquireResponse(), + } + ctx.Request.SetRequestURI(uri) + fmt.Printf("Start: %v\n", time.Now()) + start := time.Now() + testHandler(ctx) + end := time.Now() + fmt.Printf("Done: %v\n", time.Now()) + if ctx.Response.StatusCode() != status { + t.Errorf("request failed with status code %d", ctx.Response.StatusCode()) + } else { + t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + } } - ctx.Response.Reset() - ctx.Response.ResetBody() - fmt.Printf("Start: %v\n", time.Now()) - start = time.Now() - testHandler(ctx) - end = time.Now() - fmt.Printf("Done: %v\n", time.Now()) - if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 2048 { - t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) - } else { - t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) - } - - ctx.Response.Reset() - ctx.Response.ResetBody() - ctx.Request.SetRequestURI("http://example.momar.xyz/") - fmt.Printf("Start: %v\n", time.Now()) - start = time.Now() - testHandler(ctx) - end = time.Now() - fmt.Printf("Done: %v\n", time.Now()) - if ctx.Response.StatusCode() != 200 || len(ctx.Response.Body()) < 1 { - t.Errorf("request failed with status code %d and body length %d", ctx.Response.StatusCode(), len(ctx.Response.Body())) - } else { - t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) - } + testCase("https://mondstern.codeberg.page/", 424) // TODO: expect 200 + testCase("https://mondstern.codeberg.page/", 424) // TODO: expect 200 + testCase("https://example.momar.xyz/", 424) // TODO: expect 200 + testCase("https://codeberg.page/", 424) // TODO: expect 200 } From a2c5376d9ac5db328d407d6ba4bd7f21c107df51 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sun, 10 Apr 2022 18:11:00 +0200 Subject: [PATCH 108/320] Fix CORS / add Access-Control-Allow-Origin * to all methods (#69) The header is not only necessary on the OPTIONS request, but on any method, so I removed the condition. Serving any workadventure map was broken BTW. We should have tested this :-( Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/69 Reviewed-by: Andreas Shimokawa Co-authored-by: Otto Richter Co-committed-by: Otto Richter --- server/handler.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/server/handler.go b/server/handler.go index 60f5809..8445850 100644 --- a/server/handler.go +++ b/server/handler.go @@ -54,19 +54,19 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Allow CORS for specified domains + allowCors := false + for _, allowedCorsDomain := range allowedCorsDomains { + if bytes.Equal(trimmedHost, allowedCorsDomain) { + allowCors = true + break + } + } + if allowCors { + ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") + ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") + } + ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") if ctx.IsOptions() { - allowCors := false - for _, allowedCorsDomain := range allowedCorsDomains { - if bytes.Equal(trimmedHost, allowedCorsDomain) { - allowCors = true - break - } - } - if allowCors { - ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") - ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") - } - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) return } From 4267d54a6302f1acb92ec2e4d382b2ddaae3a520 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Wed, 20 Apr 2022 23:42:01 +0200 Subject: [PATCH 109/320] refactor (2) (#34) move forward with refactoring: - initial implementation of a smal "gitea client for fasthttp" - move constant into const.go Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/34 Reviewed-by: Otto Richter --- server/upstream/const.go | 2 ++ server/upstream/domains.go | 22 ++++++------ server/upstream/gitea.go | 67 +++++++++++++++++++++++++++++++++++++ server/upstream/helper.go | 30 +++++++---------- server/upstream/upstream.go | 19 ++++++----- 5 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 server/upstream/gitea.go diff --git a/server/upstream/const.go b/server/upstream/const.go index 77f64dd..ce76e21 100644 --- a/server/upstream/const.go +++ b/server/upstream/const.go @@ -19,3 +19,5 @@ var fileCacheSizeLimit = 1024 * 1024 // canonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. var canonicalDomainCacheTimeout = 15 * time.Minute + +const canonicalDomainConfig = ".domains" diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 47a5564..28a2d9c 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -3,15 +3,19 @@ package upstream import ( "strings" - "github.com/valyala/fasthttp" - "codeberg.org/codeberg/pages/server/cache" ) // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken string, canonicalDomainCache cache.SetGetKey) (string, bool) { - domains := []string{} - valid := false + return checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken, canonicalDomainCache) +} + +func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken string, canonicalDomainCache cache.SetGetKey) (string, bool) { + var ( + domains []string + valid bool + ) if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { domains = cachedValue.([]string) for _, domain := range domains { @@ -21,13 +25,9 @@ func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } } } else { - req := fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + targetOwner + "/" + targetRepo + "/raw/" + targetBranch + "/.domains" + "?access_token=" + giteaAPIToken) - res := fasthttp.AcquireResponse() - - err := client.Do(req, res) - if err == nil && res.StatusCode() == fasthttp.StatusOK { - for _, domain := range strings.Split(string(res.Body()), "\n") { + body, err := giteaRawContent(giteaRoot, targetRepo, targetBranch, giteaRoot, giteaAPIToken, canonicalDomainConfig) + if err == nil { + for _, domain := range strings.Split(string(body), "\n") { domain = strings.ToLower(domain) domain = strings.TrimSpace(domain) domain = strings.TrimPrefix(domain, "http://") diff --git a/server/upstream/gitea.go b/server/upstream/gitea.go new file mode 100644 index 0000000..eeeb0a6 --- /dev/null +++ b/server/upstream/gitea.go @@ -0,0 +1,67 @@ +package upstream + +import ( + "fmt" + "net/url" + "path" + "time" + + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" +) + +const giteaAPIRepos = "/api/v1/repos/" + +// TODOs: +// * own client to store token & giteaRoot +// * handle 404 -> page will show 500 atm + +func giteaRawContent(targetOwner, targetRepo, ref, giteaRoot, giteaAPIToken, resource string) ([]byte, error) { + req := fasthttp.AcquireRequest() + + req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref))) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) + res := fasthttp.AcquireResponse() + + if err := getFastHTTPClient(10*time.Second).Do(req, res); err != nil { + return nil, err + } + if res.StatusCode() != fasthttp.StatusOK { + return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return res.Body(), nil +} + +func giteaGetRepoBranchTimestamp(giteaRoot, repoOwner, repoName, branchName, giteaAPIToken string) (time.Time, error) { + client := getFastHTTPClient(5 * time.Second) + + req := fasthttp.AcquireRequest() + req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, repoOwner, repoName, "branches", branchName)) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) + res := fasthttp.AcquireResponse() + + if err := client.Do(req, res); err != nil { + return time.Time{}, err + } + if res.StatusCode() != fasthttp.StatusOK { + return time.Time{}, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return time.Parse(time.RFC3339, fastjson.GetString(res.Body(), "commit", "timestamp")) +} + +func giteaGetRepoDefaultBranch(giteaRoot, repoOwner, repoName, giteaAPIToken string) (string, error) { + client := getFastHTTPClient(5 * time.Second) + + req := fasthttp.AcquireRequest() + req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, repoOwner, repoName)) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) + res := fasthttp.AcquireResponse() + + if err := client.Do(req, res); err != nil { + return "", err + } + if res.StatusCode() != fasthttp.StatusOK { + return "", fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return fastjson.GetString(res.Body(), "default_branch"), nil +} diff --git a/server/upstream/helper.go b/server/upstream/helper.go index f61d746..3b51479 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -3,9 +3,6 @@ package upstream import ( "time" - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" - "codeberg.org/codeberg/pages/server/cache" ) @@ -16,34 +13,31 @@ type branchTimestamp struct { // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) -func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaApiToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { +func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { return nil } return result.(*branchTimestamp) } - result := &branchTimestamp{} - result.Branch = branch - if branch == "" { + result := &branchTimestamp{ + Branch: branch, + } + if len(branch) == 0 { // Get default branch - body := make([]byte, 0) - // TODO: use header for API key? - status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, nil, defaultBranchCacheTimeout) + defaultBranch, err := giteaGetRepoDefaultBranch(giteaRoot, owner, repo, giteaAPIToken) + if err != nil { + _ = branchTimestampCache.Set(owner+"/"+repo+"/", nil, defaultBranchCacheTimeout) return nil } - result.Branch = fastjson.GetString(body, "default_branch") + result.Branch = defaultBranch } - body := make([]byte, 0) - status, body, err := fasthttp.GetTimeout(body, giteaRoot+"/api/v1/repos/"+owner+"/"+repo+"/branches/"+branch+"?access_token="+giteaApiToken, 5*time.Second) - if err != nil || status != 200 { + timestamp, err := giteaGetRepoBranchTimestamp(giteaRoot, owner, repo, branch, giteaAPIToken) + if err != nil { return nil } - - result.Timestamp, _ = time.Parse(time.RFC3339, fastjson.GetString(body, "commit", "timestamp")) + result.Timestamp = timestamp _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, branchExistenceCacheTimeout) return result } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 88d3471..33c80e9 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -38,11 +38,13 @@ type Options struct { redirectIfExists string } -var client = fasthttp.Client{ - ReadTimeout: 10 * time.Second, - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! +func getFastHTTPClient(timeout time.Duration) *fasthttp.Client { + return &fasthttp.Client{ + ReadTimeout: timeout, + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! + } } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. @@ -80,7 +82,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st log.Debug().Msg("preparations") // Make a GET request to the upstream URL - uri := o.TargetOwner + "/" + o.TargetRepo + "/raw/" + o.TargetBranch + "/" + o.TargetPath + uri := path.Join(o.TargetOwner, o.TargetRepo, "raw", o.TargetBranch, o.TargetPath) var req *fasthttp.Request var res *fasthttp.Response var cachedResponse fileResponse @@ -89,10 +91,11 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st cachedResponse = cachedValue.(fileResponse) } else { req = fasthttp.AcquireRequest() - req.SetRequestURI(giteaRoot + "/api/v1/repos/" + uri + "?access_token=" + giteaAPIToken) + req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, uri)) + req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) res = fasthttp.AcquireResponse() res.SetBodyStream(&strings.Reader{}, -1) - err = client.Do(req, res) + err = getFastHTTPClient(10*time.Second).Do(req, res) } log.Debug().Msg("acquisition") From 1724d9fb2e130eec7e15d0d203a3860502386ca8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 10 May 2022 18:13:14 +0200 Subject: [PATCH 110/320] add "lint" to Justfile --- Justfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Justfile b/Justfile index 3d23175..fac93d1 100644 --- a/Justfile +++ b/Justfile @@ -10,3 +10,17 @@ dev: build: CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ + +lint: tool-golangci tool-gofumpt + [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ + golangci-lint run + +tool-golangci: + @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ + ggo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \ + fi + +tool-gofumpt: + @hash gofumpt> /dev/null 2>&1; if [ $? -ne 0 ]; then \ + go install mvdan.cc/gofumpt@latest; \ + fi From 2dbc66d052e50706be1d1ecdb8db55b701943a24 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 10 May 2022 18:14:28 +0200 Subject: [PATCH 111/320] let golangci-lint have 5m to check --- .woodpecker.yml | 2 +- Justfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 76d9f3a..d6f3319 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -11,7 +11,7 @@ pipeline: - go version - go install mvdan.cc/gofumpt@latest - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - - golangci-lint run + - golangci-lint run --timeout 5m test: image: golang diff --git a/Justfile b/Justfile index fac93d1..8d31fa9 100644 --- a/Justfile +++ b/Justfile @@ -13,7 +13,7 @@ build: lint: tool-golangci tool-gofumpt [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ - golangci-lint run + golangci-lint run --timeout 5m tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ From b2ca88805069700e59c7bca71e3aecd91c783e72 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sat, 14 May 2022 22:29:54 +0200 Subject: [PATCH 112/320] Change MaxConnsPerIP to 0 to fix too many connections from HAProxy (#77) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/77 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Moritz Marquardt Co-committed-by: Moritz Marquardt --- server/setup.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/setup.go b/server/setup.go index 67c1c42..c186222 100644 --- a/server/setup.go +++ b/server/setup.go @@ -18,12 +18,10 @@ func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { return &fasthttp.Server{ Handler: compressedHandler, DisablePreParseMultipartForm: true, - MaxRequestBodySize: 0, NoDefaultServerHeader: true, NoDefaultDate: true, ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! - MaxConnsPerIP: 100, } } From 6f12f2a8e46a4781dd1653be10addd340cfbb4e0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 15 May 2022 22:36:12 +0200 Subject: [PATCH 113/320] fix bug --- server/upstream/domains.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 28a2d9c..8669d08 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -25,7 +25,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } } } else { - body, err := giteaRawContent(giteaRoot, targetRepo, targetBranch, giteaRoot, giteaAPIToken, canonicalDomainConfig) + body, err := giteaRawContent(targetOwner, targetRepo, targetBranch, giteaRoot, giteaAPIToken, canonicalDomainConfig) if err == nil { for _, domain := range strings.Split(string(body), "\n") { domain = strings.ToLower(domain) From 57076a47d39aece27ddd7dbaa3fdb717f0d3076d Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Mon, 30 May 2022 23:55:37 +0200 Subject: [PATCH 114/320] Update 'Justfile' --- Justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Justfile b/Justfile index 8d31fa9..2c64574 100644 --- a/Justfile +++ b/Justfile @@ -17,7 +17,7 @@ lint: tool-golangci tool-gofumpt tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ - ggo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \ + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \ fi tool-gofumpt: From f2ba7eac6452f8ef79035240efbc279fcb79421e Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 10 Jun 2022 15:27:17 +0200 Subject: [PATCH 115/320] set golang to 1.18 (#84) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/84 --- .woodpecker.yml | 6 +-- go.mod | 113 +++++++++++++++++++++++++++++++++++++++++++++++- go.sum | 1 - 3 files changed, 115 insertions(+), 5 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index d6f3319..0440309 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,7 +1,7 @@ pipeline: # use vendor to cache dependencies vendor: - image: golang + image: golang:1.18 commands: - go mod vendor @@ -14,11 +14,11 @@ pipeline: - golangci-lint run --timeout 5m test: - image: golang + image: golang:1.18 commands: - go test ./... build: - image: golang + image: golang:1.18 commands: - go build diff --git a/go.mod b/go.mod index 615bb12..38a289a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module codeberg.org/codeberg/pages -go 1.16 +go 1.18 require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a @@ -13,3 +13,114 @@ require ( github.com/valyala/fasthttp v1.31.0 github.com/valyala/fastjson v1.6.3 ) + +require ( + cloud.google.com/go v0.54.0 // indirect + github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect + github.com/Azure/go-autorest v14.2.0+incompatible // indirect + github.com/Azure/go-autorest/autorest v0.11.19 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect + github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect + github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect + github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect + github.com/Azure/go-autorest/logger v0.2.1 // indirect + github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect + github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/aws/aws-sdk-go v1.39.0 // indirect + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect + github.com/cenkalti/backoff/v4 v4.1.1 // indirect + github.com/cloudflare/cloudflare-go v0.20.0 // indirect + github.com/cpu/goacmedns v0.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deepmap/oapi-codegen v1.6.1 // indirect + github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/dnsimple/dnsimple-go v0.70.1 // indirect + github.com/exoscale/egoscale v0.67.0 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect + github.com/go-errors/errors v1.0.1 // indirect + github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect + github.com/gofrs/uuid v3.2.0+incompatible // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.1.1 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/gophercloud/gophercloud v0.16.0 // indirect + github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect + github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect + github.com/jarcoal/httpmock v1.0.6 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.7 // indirect + github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect + github.com/klauspost/compress v1.13.4 // indirect + github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect + github.com/labbsr0x/goh v1.0.1 // indirect + github.com/linode/linodego v0.31.1 // indirect + github.com/liquidweb/go-lwApi v0.0.5 // indirect + github.com/liquidweb/liquidweb-cli v0.6.9 // indirect + github.com/liquidweb/liquidweb-go v1.6.3 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/miekg/dns v1.1.43 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect + github.com/nrdcg/auroradns v1.0.1 // indirect + github.com/nrdcg/desec v0.6.0 // indirect + github.com/nrdcg/dnspod-go v0.4.0 // indirect + github.com/nrdcg/freemyip v0.2.0 // indirect + github.com/nrdcg/goinwx v0.8.1 // indirect + github.com/nrdcg/namesilo v0.2.1 // indirect + github.com/nrdcg/porkbun v0.1.1 // indirect + github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect + github.com/ovh/go-ovh v1.1.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/otp v1.3.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/sacloud/libsacloud v1.36.2 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/sirupsen/logrus v1.4.2 // indirect + github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect + github.com/softlayer/softlayer-go v1.0.3 // indirect + github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/stretchr/objx v0.3.0 // indirect + github.com/transip/gotransip/v6 v6.6.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect + github.com/vultr/govultr/v2 v2.7.1 // indirect + go.opencensus.io v0.22.3 // indirect + go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect + golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect + golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect + golang.org/x/text v0.3.6 // indirect + golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect + google.golang.org/api v0.20.0 // indirect + google.golang.org/appengine v1.6.5 // indirect + google.golang.org/genproto v0.0.0-20200305110556-506484158171 // indirect + google.golang.org/grpc v1.27.1 // indirect + google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) diff --git a/go.sum b/go.sum index d04f727..a443641 100644 --- a/go.sum +++ b/go.sum @@ -486,7 +486,6 @@ github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggs github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= From bb8eb32ee2be5d63dd31edc8d4367b6c21761867 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 10 Jun 2022 15:25:33 +0200 Subject: [PATCH 116/320] make debug messages unique --- server/handler.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/handler.go b/server/handler.go index 8445850..0dc90ce 100644 --- a/server/handler.go +++ b/server/handler.go @@ -135,7 +135,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 1") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -151,7 +151,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, tryBranch(targetRepo, "", pathElements[2:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 2") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -186,7 +186,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 3") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -202,7 +202,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, if strings.HasPrefix(pathElements[0], "@") { log.Debug().Msg("main domain preparations, now trying with specified branch") if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 4") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -218,7 +218,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // example.codeberg.page/pages/... is not allowed here. log.Debug().Msg("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 5") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -230,7 +230,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // example.codeberg.page/index.html log.Debug().Msg("main domain preparations, now trying with default repo/branch") if tryBranch("pages", "", pathElements, "") { - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 6") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, @@ -278,7 +278,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, return } - log.Debug().Msg("tryBranch, now trying upstream") + log.Debug().Msg("tryBranch, now trying upstream 7") tryUpstream(ctx, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, giteaRoot, giteaAPIToken, From 659932521ce66acc16a8a7a80bed29c292532aee Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 10 Jun 2022 20:17:07 +0200 Subject: [PATCH 117/320] Add info how to test & debug the server (#85) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/85 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index b56239b..6543c90 100644 --- a/README.md +++ b/README.md @@ -97,3 +97,12 @@ After scanning the code, you should quickly be able to understand their function Again: Feel free to get in touch with us for any questions that might arise. Thank you very much. + + +### Test Server + +run `just dev` +now this pages should work: + - https://magiclike.localhost.mock.directory:4430/ + - https://momar.localhost.mock.directory:4430/ci-testing/ + - https://momar.localhost.mock.directory:4430/pag/@master/ \ No newline at end of file From 02bd942b04033ab3d6c11b279b42440bac3c5739 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Sat, 11 Jun 2022 23:02:06 +0200 Subject: [PATCH 118/320] Move gitea api calls in own "client" package (#78) continue #75 close #16 - fix regression (from #34) _thanks to @crystal_ - create own gitea client package - more logging - add mock impl of CertDB Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: crystal Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/78 Reviewed-by: crapStone --- .woodpecker.yml | 2 + Justfile | 3 + cmd/certs.go | 2 +- cmd/main.go | 11 ++- server/certificates/certificates.go | 18 +++-- server/certificates/mock_test.go | 17 ++++ server/database/interface.go | 6 +- server/database/mock.go | 55 +++++++++++++ server/database/setup.go | 16 ++-- server/gitea/client.go | 119 ++++++++++++++++++++++++++++ server/gitea/client_test.go | 23 ++++++ server/gitea/fasthttp.go | 15 ++++ server/handler.go | 65 ++++++++------- server/handler_test.go | 11 +-- server/try.go | 10 +-- server/upstream/domains.go | 9 +-- server/upstream/gitea.go | 67 ---------------- server/upstream/helper.go | 37 +++++++-- server/upstream/upstream.go | 82 +++++++------------ 19 files changed, 374 insertions(+), 194 deletions(-) create mode 100644 server/certificates/mock_test.go create mode 100644 server/database/mock.go create mode 100644 server/gitea/client.go create mode 100644 server/gitea/client_test.go create mode 100644 server/gitea/fasthttp.go delete mode 100644 server/upstream/gitea.go diff --git a/.woodpecker.yml b/.woodpecker.yml index 0440309..271aaca 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,3 +1,5 @@ +branches: main + pipeline: # use vendor to cache dependencies vendor: diff --git a/Justfile b/Justfile index 2c64574..bab0a1e 100644 --- a/Justfile +++ b/Justfile @@ -15,6 +15,9 @@ lint: tool-golangci tool-gofumpt [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ golangci-lint run --timeout 5m +fmt: tool-gofumpt + gofumpt -w --extra . + tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \ diff --git a/cmd/certs.go b/cmd/certs.go index 83f2ac5..d93fe13 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -61,7 +61,7 @@ func removeCert(ctx *cli.Context) error { for _, domain := range domains { fmt.Printf("Removing domain %s from the database...\n", domain) - if err := keyDatabase.Delete([]byte(domain)); err != nil { + if err := keyDatabase.Delete(domain); err != nil { return err } } diff --git a/cmd/main.go b/cmd/main.go index 6836cee..257b724 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,6 +18,7 @@ import ( "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/database" + "codeberg.org/codeberg/pages/server/gitea" ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. @@ -81,9 +82,12 @@ func Serve(ctx *cli.Context) error { // TODO: make this an MRU cache with a size limit fileResponseCache := cache.NewKeyValueCache() + giteaClient := gitea.NewClient(giteaRoot, giteaAPIToken) + // Create handler based on settings handler := server.Handler(mainDomainSuffix, []byte(rawDomain), - giteaRoot, rawInfoPage, giteaAPIToken, + giteaClient, + giteaRoot, rawInfoPage, BlacklistedPaths, allowedCorsDomains, dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache) @@ -105,7 +109,8 @@ func Serve(ctx *cli.Context) error { defer certDB.Close() //nolint:errcheck // database has no close ... sync behave like it listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, - giteaRoot, giteaAPIToken, dnsProvider, + giteaClient, + dnsProvider, acmeUseRateLimits, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, certDB)) @@ -126,6 +131,7 @@ func Serve(ctx *cli.Context) error { if enableHTTPServer { go func() { + log.Info().Timestamp().Msg("Start listening on :80") err := httpServer.ListenAndServe("[::]:80") if err != nil { log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") @@ -134,6 +140,7 @@ func Serve(ctx *cli.Context) error { } // Start the web fastServer + log.Info().Timestamp().Msgf("Start listening on %s", listener.Addr()) err = fastServer.Serve(listener) if err != nil { log.Panic().Err(err).Msg("Couldn't start fastServer") diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index a13215c..2684dfa 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -32,12 +32,14 @@ import ( "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/database" dnsutils "codeberg.org/codeberg/pages/server/dns" + "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. func TLSConfig(mainDomainSuffix []byte, - giteaRoot, giteaAPIToken, dnsProvider string, + giteaClient *gitea.Client, + dnsProvider string, acmeUseRateLimits bool, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, certDB database.CertDB, @@ -81,7 +83,7 @@ func TLSConfig(mainDomainSuffix []byte, sni = string(sniBytes) } else { _, _ = targetRepo, targetBranch - _, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) + _, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), canonicalDomainCache) if !valid { sniBytes = mainDomainSuffix sni = string(sniBytes) @@ -193,7 +195,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { // parse certificate from database - res, err := certDB.Get(sni) + res, err := certDB.Get(string(sni)) if err != nil { panic(err) // TODO: no panic } @@ -406,7 +408,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits - mainCertBytes, err := certDB.Get(mainDomainSuffix) + mainCertBytes, err := certDB.Get(string(mainDomainSuffix)) if err != nil { return fmt.Errorf("cert database is not working") } @@ -478,7 +480,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) if err != nil || !tlsCertificates[0].NotAfter.After(now) { - err := certDB.Delete(key) + err := certDB.Delete(string(key)) if err != nil { log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) } else { @@ -491,15 +493,15 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) // compact the database - result, err := certDB.Compact() + msg, err := certDB.Compact() if err != nil { log.Printf("[ERROR] Compacting key database failed: %s", err) } else { - log.Printf("[INFO] Compacted key database (%+v)", result) + log.Printf("[INFO] Compacted key database (%s)", msg) } // update main cert - res, err := certDB.Get(mainDomainSuffix) + res, err := certDB.Get(string(mainDomainSuffix)) if err != nil { log.Err(err).Msgf("could not get cert for domain '%s'", mainDomainSuffix) } else if res == nil { diff --git a/server/certificates/mock_test.go b/server/certificates/mock_test.go new file mode 100644 index 0000000..1cbd1f6 --- /dev/null +++ b/server/certificates/mock_test.go @@ -0,0 +1,17 @@ +package certificates + +import ( + "testing" + + "codeberg.org/codeberg/pages/server/database" + "github.com/stretchr/testify/assert" +) + +func TestMockCert(t *testing.T) { + db, err := database.NewTmpDB() + assert.NoError(t, err) + cert := mockCert("example.com", "some error msg", "codeberg.page", db) + if assert.NotEmpty(t, cert) { + assert.NotEmpty(t, cert.Certificate) + } +} diff --git a/server/database/interface.go b/server/database/interface.go index 01b9872..3ba3efc 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -8,8 +8,8 @@ import ( type CertDB interface { Close() error Put(name string, cert *certificate.Resource) error - Get(name []byte) (*certificate.Resource, error) - Delete(key []byte) error - Compact() (pogreb.CompactionResult, error) + Get(name string) (*certificate.Resource, error) + Delete(key string) error + Compact() (string, error) Items() *pogreb.ItemIterator } diff --git a/server/database/mock.go b/server/database/mock.go new file mode 100644 index 0000000..e6c1b5a --- /dev/null +++ b/server/database/mock.go @@ -0,0 +1,55 @@ +package database + +import ( + "fmt" + "time" + + "github.com/OrlovEvgeny/go-mcache" + "github.com/akrylysov/pogreb" + "github.com/go-acme/lego/v4/certificate" +) + +var _ CertDB = tmpDB{} + +type tmpDB struct { + intern *mcache.CacheDriver + ttl time.Duration +} + +func (p tmpDB) Close() error { + _ = p.intern.Close() + return nil +} + +func (p tmpDB) Put(name string, cert *certificate.Resource) error { + return p.intern.Set(name, cert, p.ttl) +} + +func (p tmpDB) Get(name string) (*certificate.Resource, error) { + cert, has := p.intern.Get(name) + if !has { + return nil, fmt.Errorf("cert for '%s' not found", name) + } + return cert.(*certificate.Resource), nil +} + +func (p tmpDB) Delete(key string) error { + p.intern.Remove(key) + return nil +} + +func (p tmpDB) Compact() (string, error) { + p.intern.Truncate() + return "Truncate done", nil +} + +func (p tmpDB) Items() *pogreb.ItemIterator { + panic("ItemIterator not implemented for tmpDB") +} + +func NewTmpDB() (CertDB, error) { + return &tmpDB{ + intern: mcache.New(), + ttl: time.Minute, + }, nil +} diff --git a/server/database/setup.go b/server/database/setup.go index e48b661..bbcf431 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -35,9 +35,9 @@ func (p aDB) Put(name string, cert *certificate.Resource) error { return p.intern.Put([]byte(name), resGob.Bytes()) } -func (p aDB) Get(name []byte) (*certificate.Resource, error) { +func (p aDB) Get(name string) (*certificate.Resource, error) { cert := &certificate.Resource{} - resBytes, err := p.intern.Get(name) + resBytes, err := p.intern.Get([]byte(name)) if err != nil { return nil, err } @@ -50,12 +50,16 @@ func (p aDB) Get(name []byte) (*certificate.Resource, error) { return cert, nil } -func (p aDB) Delete(key []byte) error { - return p.intern.Delete(key) +func (p aDB) Delete(key string) error { + return p.intern.Delete([]byte(key)) } -func (p aDB) Compact() (pogreb.CompactionResult, error) { - return p.intern.Compact() +func (p aDB) Compact() (string, error) { + result, err := p.intern.Compact() + if err != nil { + return "", err + } + return fmt.Sprintf("%+v", result), nil } func (p aDB) Items() *pogreb.ItemIterator { diff --git a/server/gitea/client.go b/server/gitea/client.go new file mode 100644 index 0000000..d4eb980 --- /dev/null +++ b/server/gitea/client.go @@ -0,0 +1,119 @@ +package gitea + +import ( + "errors" + "fmt" + "net/url" + "path" + "time" + + "github.com/valyala/fasthttp" + "github.com/valyala/fastjson" +) + +const giteaAPIRepos = "/api/v1/repos/" + +var ErrorNotFound = errors.New("not found") + +type Client struct { + giteaRoot string + giteaAPIToken string + fastClient *fasthttp.Client + infoTimeout time.Duration + contentTimeout time.Duration +} + +type FileResponse struct { + Exists bool + MimeType string + Body []byte +} + +func joinURL(giteaRoot string, paths ...string) string { return giteaRoot + path.Join(paths...) } + +func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } + +func NewClient(giteaRoot, giteaAPIToken string) *Client { + return &Client{ + giteaRoot: giteaRoot, + giteaAPIToken: giteaAPIToken, + infoTimeout: 5 * time.Second, + contentTimeout: 10 * time.Second, + fastClient: getFastHTTPClient(), + } +} + +func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { + url := joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref)) + res, err := client.do(client.contentTimeout, url) + if err != nil { + return nil, err + } + + switch res.StatusCode() { + case fasthttp.StatusOK: + return res.Body(), nil + case fasthttp.StatusNotFound: + return nil, ErrorNotFound + default: + return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } +} + +func (client *Client) ServeRawContent(uri string) (*fasthttp.Response, error) { + url := joinURL(client.giteaRoot, giteaAPIRepos, uri) + res, err := client.do(client.contentTimeout, url) + if err != nil { + return nil, err + } + // resp.SetBodyStream(&strings.Reader{}, -1) + + if err != nil { + return nil, err + } + + switch res.StatusCode() { + case fasthttp.StatusOK: + return res, nil + case fasthttp.StatusNotFound: + return nil, ErrorNotFound + default: + return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } +} + +func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (time.Time, error) { + url := joinURL(client.giteaRoot, giteaAPIRepos, repoOwner, repoName, "branches", branchName) + res, err := client.do(client.infoTimeout, url) + if err != nil { + return time.Time{}, err + } + if res.StatusCode() != fasthttp.StatusOK { + return time.Time{}, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return time.Parse(time.RFC3339, fastjson.GetString(res.Body(), "commit", "timestamp")) +} + +func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) { + url := joinURL(client.giteaRoot, giteaAPIRepos, repoOwner, repoName) + res, err := client.do(client.infoTimeout, url) + if err != nil { + return "", err + } + if res.StatusCode() != fasthttp.StatusOK { + return "", fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + } + return fastjson.GetString(res.Body(), "default_branch"), nil +} + +func (client *Client) do(timeout time.Duration, url string) (*fasthttp.Response, error) { + req := fasthttp.AcquireRequest() + + req.SetRequestURI(url) + req.Header.Set(fasthttp.HeaderAuthorization, "token "+client.giteaAPIToken) + res := fasthttp.AcquireResponse() + + err := client.fastClient.DoTimeout(req, res, timeout) + + return res, err +} diff --git a/server/gitea/client_test.go b/server/gitea/client_test.go new file mode 100644 index 0000000..bae9d4e --- /dev/null +++ b/server/gitea/client_test.go @@ -0,0 +1,23 @@ +package gitea + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestJoinURL(t *testing.T) { + url := joinURL("") + assert.EqualValues(t, "", url) + + url = joinURL("", "", "") + assert.EqualValues(t, "", url) + + url = joinURL("http://wwow.url.com", "a", "b/c/", "d") + // assert.EqualValues(t, "http://wwow.url.com/a/b/c/d", url) + assert.EqualValues(t, "http://wwow.url.coma/b/c/d", url) + + url = joinURL("h:://wrong", "acdc") + // assert.EqualValues(t, "h:://wrong/acdc", url) + assert.EqualValues(t, "h:://wrongacdc", url) +} diff --git a/server/gitea/fasthttp.go b/server/gitea/fasthttp.go new file mode 100644 index 0000000..4ff0f4a --- /dev/null +++ b/server/gitea/fasthttp.go @@ -0,0 +1,15 @@ +package gitea + +import ( + "time" + + "github.com/valyala/fasthttp" +) + +func getFastHTTPClient() *fasthttp.Client { + return &fasthttp.Client{ + MaxConnDuration: 60 * time.Second, + MaxConnWaitTimeout: 1000 * time.Millisecond, + MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! + } +} diff --git a/server/handler.go b/server/handler.go index 0dc90ce..bda7bd0 100644 --- a/server/handler.go +++ b/server/handler.go @@ -4,19 +4,22 @@ import ( "bytes" "strings" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/dns" + "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" "codeberg.org/codeberg/pages/server/utils" ) // Handler handles a single HTTP request to the web server. func Handler(mainDomainSuffix, rawDomain []byte, - giteaRoot, rawInfoPage, giteaAPIToken string, + giteaClient *gitea.Client, + giteaRoot, rawInfoPage string, blacklistedPaths, allowedCorsDomains [][]byte, dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, ) func(ctx *fasthttp.RequestCtx) { @@ -74,21 +77,21 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Prepare request information to Gitea var targetOwner, targetRepo, targetBranch, targetPath string targetOptions := &upstream.Options{ - ForbiddenMimeTypes: map[string]struct{}{}, - TryIndexPages: true, + TryIndexPages: true, } // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will // also disallow search indexing and add a Link header to the canonical URL. - tryBranch := func(repo, branch string, path []string, canonicalLink string) bool { + tryBranch := func(log zerolog.Logger, repo, branch string, path []string, canonicalLink string) bool { if repo == "" { + log.Debug().Msg("tryBranch: repo == ''") return false } // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := upstream.GetBranchTimestamp(targetOwner, repo, branch, giteaRoot, giteaAPIToken, branchTimestampCache) + branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch, branchTimestampCache) if branchTimestampResult == nil { - // branch doesn't exist + log.Debug().Msg("tryBranch: branch doesn't exist") return false } @@ -108,6 +111,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, ) } + log.Debug().Msg("tryBranch: true") return true } @@ -117,7 +121,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("raw domain") targetOptions.TryIndexPages = false - targetOptions.ForbiddenMimeTypes["text/html"] = struct{}{} + if targetOptions.ForbiddenMimeTypes == nil { + targetOptions.ForbiddenMimeTypes = make(map[string]bool) + } + targetOptions.ForbiddenMimeTypes["text/html"] = true targetOptions.DefaultMimeType = "text/plain; charset=utf-8" pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") @@ -132,13 +139,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, // raw.codeberg.org/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { log.Debug().Msg("raw domain preparations, now trying with specified branch") - if tryBranch(targetRepo, pathElements[2][1:], pathElements[3:], + if tryBranch(log, + targetRepo, pathElements[2][1:], pathElements[3:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { log.Debug().Msg("tryBranch, now trying upstream 1") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -148,13 +155,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, } log.Debug().Msg("raw domain preparations, now trying with default branch") - tryBranch(targetRepo, "", pathElements[2:], + tryBranch(log, + targetRepo, "", pathElements[2:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) log.Debug().Msg("tryBranch, now trying upstream 2") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return @@ -183,13 +190,13 @@ func Handler(mainDomainSuffix, rawDomain []byte, } log.Debug().Msg("main domain preparations, now trying with specified repo & branch") - if tryBranch(pathElements[0], pathElements[1][1:], pathElements[2:], + if tryBranch(log, + pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { log.Debug().Msg("tryBranch, now trying upstream 3") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -201,11 +208,11 @@ func Handler(mainDomainSuffix, rawDomain []byte, // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { log.Debug().Msg("main domain preparations, now trying with specified branch") - if tryBranch("pages", pathElements[0][1:], pathElements[1:], "/%p") { + if tryBranch(log, + "pages", pathElements[0][1:], pathElements[1:], "/%p") { log.Debug().Msg("tryBranch, now trying upstream 4") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -217,11 +224,11 @@ func Handler(mainDomainSuffix, rawDomain []byte, // example.codeberg.page/myrepo/index.html // example.codeberg.page/pages/... is not allowed here. log.Debug().Msg("main domain preparations, now trying with specified repo") - if pathElements[0] != "pages" && tryBranch(pathElements[0], "pages", pathElements[1:], "") { + if pathElements[0] != "pages" && tryBranch(log, + pathElements[0], "pages", pathElements[1:], "") { log.Debug().Msg("tryBranch, now trying upstream 5") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -229,11 +236,11 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Try to use the "pages" repo on its default branch // example.codeberg.page/index.html log.Debug().Msg("main domain preparations, now trying with default repo/branch") - if tryBranch("pages", "", pathElements, "") { + if tryBranch(log, + "pages", "", pathElements, "") { log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } @@ -261,8 +268,9 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Try to use the given repo on the given branch or the default branch log.Debug().Msg("custom domain preparations, now trying with details from DNS") - if tryBranch(targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) + if tryBranch(log, + targetRepo, targetBranch, pathElements, canonicalLink) { + canonicalDomain, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return @@ -279,9 +287,8 @@ func Handler(mainDomainSuffix, rawDomain []byte, } log.Debug().Msg("tryBranch, now trying upstream 7") - tryUpstream(ctx, mainDomainSuffix, trimmedHost, + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - giteaRoot, giteaAPIToken, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } diff --git a/server/handler_test.go b/server/handler_test.go index 3b4d21a..73002a2 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -8,15 +8,16 @@ import ( "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/gitea" ) func TestHandlerPerformance(t *testing.T) { + giteaRoot := "https://codeberg.org" + giteaClient := gitea.NewClient(giteaRoot, "") testHandler := Handler( - []byte("codeberg.page"), - []byte("raw.codeberg.org"), - "https://codeberg.org", - "https://docs.codeberg.org/pages/raw-content/", - "", + []byte("codeberg.page"), []byte("raw.codeberg.org"), + giteaClient, + giteaRoot, "https://docs.codeberg.org/pages/raw-content/", [][]byte{[]byte("/.well-known/acme-challenge/")}, [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, cache.NewKeyValueCache(), diff --git a/server/try.go b/server/try.go index 4eda5b2..254d3ec 100644 --- a/server/try.go +++ b/server/try.go @@ -8,22 +8,22 @@ import ( "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" ) // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. -func tryUpstream(ctx *fasthttp.RequestCtx, +func tryUpstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, mainDomainSuffix, trimmedHost []byte, targetOptions *upstream.Options, - targetOwner, targetRepo, targetBranch, targetPath, + targetOwner, targetRepo, targetBranch, targetPath string, - giteaRoot, giteaAPIToken string, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, ) { // check if a canonical domain exists on a request on MainDomain if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := upstream.CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), giteaRoot, giteaAPIToken, canonicalDomainCache) + canonicalDomain, _ := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := string(ctx.RequestURI()) if targetRepo != "pages" { @@ -43,7 +43,7 @@ func tryUpstream(ctx *fasthttp.RequestCtx, targetOptions.TargetPath = targetPath // Try to request the file from the Gitea API - if !targetOptions.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { + if !targetOptions.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) } } diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 8669d08..553c148 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -4,14 +4,11 @@ import ( "strings" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/gitea" ) // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). -func CheckCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken string, canonicalDomainCache cache.SetGetKey) (string, bool) { - return checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken, canonicalDomainCache) -} - -func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix, giteaRoot, giteaAPIToken string, canonicalDomainCache cache.SetGetKey) (string, bool) { +func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (string, bool) { var ( domains []string valid bool @@ -25,7 +22,7 @@ func checkCanonicalDomain(targetOwner, targetRepo, targetBranch, actualDomain, m } } } else { - body, err := giteaRawContent(targetOwner, targetRepo, targetBranch, giteaRoot, giteaAPIToken, canonicalDomainConfig) + body, err := giteaClient.GiteaRawContent(targetOwner, targetRepo, targetBranch, canonicalDomainConfig) if err == nil { for _, domain := range strings.Split(string(body), "\n") { domain = strings.ToLower(domain) diff --git a/server/upstream/gitea.go b/server/upstream/gitea.go deleted file mode 100644 index eeeb0a6..0000000 --- a/server/upstream/gitea.go +++ /dev/null @@ -1,67 +0,0 @@ -package upstream - -import ( - "fmt" - "net/url" - "path" - "time" - - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" -) - -const giteaAPIRepos = "/api/v1/repos/" - -// TODOs: -// * own client to store token & giteaRoot -// * handle 404 -> page will show 500 atm - -func giteaRawContent(targetOwner, targetRepo, ref, giteaRoot, giteaAPIToken, resource string) ([]byte, error) { - req := fasthttp.AcquireRequest() - - req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref))) - req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) - res := fasthttp.AcquireResponse() - - if err := getFastHTTPClient(10*time.Second).Do(req, res); err != nil { - return nil, err - } - if res.StatusCode() != fasthttp.StatusOK { - return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) - } - return res.Body(), nil -} - -func giteaGetRepoBranchTimestamp(giteaRoot, repoOwner, repoName, branchName, giteaAPIToken string) (time.Time, error) { - client := getFastHTTPClient(5 * time.Second) - - req := fasthttp.AcquireRequest() - req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, repoOwner, repoName, "branches", branchName)) - req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) - res := fasthttp.AcquireResponse() - - if err := client.Do(req, res); err != nil { - return time.Time{}, err - } - if res.StatusCode() != fasthttp.StatusOK { - return time.Time{}, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) - } - return time.Parse(time.RFC3339, fastjson.GetString(res.Body(), "commit", "timestamp")) -} - -func giteaGetRepoDefaultBranch(giteaRoot, repoOwner, repoName, giteaAPIToken string) (string, error) { - client := getFastHTTPClient(5 * time.Second) - - req := fasthttp.AcquireRequest() - req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, repoOwner, repoName)) - req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) - res := fasthttp.AcquireResponse() - - if err := client.Do(req, res); err != nil { - return "", err - } - if res.StatusCode() != fasthttp.StatusOK { - return "", fmt.Errorf("unexpected status code '%d'", res.StatusCode()) - } - return fastjson.GetString(res.Body(), "default_branch"), nil -} diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 3b51479..5bbe833 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -1,9 +1,14 @@ package upstream import ( + "mime" + "path" + "strconv" + "strings" "time" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/gitea" ) type branchTimestamp struct { @@ -13,7 +18,7 @@ type branchTimestamp struct { // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) -func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, branchTimestampCache cache.SetGetKey) *branchTimestamp { +func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string, branchTimestampCache cache.SetGetKey) *branchTimestamp { if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { return nil @@ -25,7 +30,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, br } if len(branch) == 0 { // Get default branch - defaultBranch, err := giteaGetRepoDefaultBranch(giteaRoot, owner, repo, giteaAPIToken) + defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(owner, repo) if err != nil { _ = branchTimestampCache.Set(owner+"/"+repo+"/", nil, defaultBranchCacheTimeout) return nil @@ -33,7 +38,7 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, br result.Branch = defaultBranch } - timestamp, err := giteaGetRepoBranchTimestamp(giteaRoot, owner, repo, branch, giteaAPIToken) + timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(owner, repo, result.Branch) if err != nil { return nil } @@ -42,8 +47,26 @@ func GetBranchTimestamp(owner, repo, branch, giteaRoot, giteaAPIToken string, br return result } -type fileResponse struct { - exists bool - mimeType string - body []byte +func (o *Options) getMimeTypeByExtension() string { + if o.ForbiddenMimeTypes == nil { + o.ForbiddenMimeTypes = make(map[string]bool) + } + mimeType := mime.TypeByExtension(path.Ext(o.TargetPath)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if o.ForbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { + if o.DefaultMimeType != "" { + mimeType = o.DefaultMimeType + } else { + mimeType = "application/octet-stream" + } + } + return mimeType +} + +func (o *Options) generateUri() string { + return path.Join(o.TargetOwner, o.TargetRepo, "raw", o.TargetBranch, o.TargetPath) +} + +func (o *Options) timestamp() string { + return strconv.FormatInt(o.BranchTimestamp.Unix(), 10) } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 33c80e9..edf4f3f 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -2,11 +2,9 @@ package upstream import ( "bytes" + "errors" "fmt" "io" - "mime" - "path" - "strconv" "strings" "time" @@ -15,6 +13,7 @@ import ( "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/gitea" ) // upstreamIndexPages lists pages that may be considered as index pages for directories. @@ -30,7 +29,7 @@ type Options struct { TargetPath, DefaultMimeType string - ForbiddenMimeTypes map[string]struct{} + ForbiddenMimeTypes map[string]bool TryIndexPages bool BranchTimestamp time.Time // internal @@ -38,26 +37,13 @@ type Options struct { redirectIfExists string } -func getFastHTTPClient(timeout time.Duration) *fasthttp.Client { - return &fasthttp.Client{ - ReadTimeout: timeout, - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! - } -} - // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken string, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { +func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() - if o.ForbiddenMimeTypes == nil { - o.ForbiddenMimeTypes = map[string]struct{}{} - } - // Check if the branch exists and when it was modified if o.BranchTimestamp.IsZero() { - branch := GetBranchTimestamp(o.TargetOwner, o.TargetRepo, o.TargetBranch, giteaRoot, giteaAPIToken, branchTimestampCache) + branch := GetBranchTimestamp(giteaClient, o.TargetOwner, o.TargetRepo, o.TargetBranch, branchTimestampCache) if branch == nil { html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) @@ -82,25 +68,19 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st log.Debug().Msg("preparations") // Make a GET request to the upstream URL - uri := path.Join(o.TargetOwner, o.TargetRepo, "raw", o.TargetBranch, o.TargetPath) - var req *fasthttp.Request + uri := o.generateUri() var res *fasthttp.Response - var cachedResponse fileResponse + var cachedResponse gitea.FileResponse var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + strconv.FormatInt(o.BranchTimestamp.Unix(), 10)); ok && len(cachedValue.(fileResponse).body) > 0 { - cachedResponse = cachedValue.(fileResponse) + if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + o.timestamp()); ok && !cachedValue.(gitea.FileResponse).IsEmpty() { + cachedResponse = cachedValue.(gitea.FileResponse) } else { - req = fasthttp.AcquireRequest() - req.SetRequestURI(path.Join(giteaRoot, giteaAPIRepos, uri)) - req.Header.Set(fasthttp.HeaderAuthorization, giteaAPIToken) - res = fasthttp.AcquireResponse() - res.SetBodyStream(&strings.Reader{}, -1) - err = getFastHTTPClient(10*time.Second).Do(req, res) + res, err = giteaClient.ServeRawContent(uri) } log.Debug().Msg("acquisition") // Handle errors - if (res == nil && !cachedResponse.exists) || (res != nil && res.StatusCode() == fasthttp.StatusNotFound) { + if (err != nil && errors.Is(err, gitea.ErrorNotFound)) || (res == nil && !cachedResponse.Exists) { if o.TryIndexPages { // copy the o struct & try if an index page exists optionsForIndexPages := *o @@ -108,9 +88,9 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st optionsForIndexPages.appendTrailingSlash = true for _, indexPage := range upstreamIndexPages { optionsForIndexPages.TargetPath = strings.TrimSuffix(o.TargetPath, "/") + "/" + indexPage - if optionsForIndexPages.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, + if optionsForIndexPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { + _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ + Exists: false, }, fileCacheTimeout) return true } @@ -119,9 +99,9 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st optionsForIndexPages.appendTrailingSlash = false optionsForIndexPages.redirectIfExists = strings.TrimSuffix(string(ctx.Request.URI().Path()), "/") + ".html" optionsForIndexPages.TargetPath = o.TargetPath + ".html" - if optionsForIndexPages.Upstream(ctx, giteaRoot, giteaAPIToken, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, + if optionsForIndexPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { + _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ + Exists: false, }, fileCacheTimeout) return true } @@ -129,14 +109,14 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st ctx.Response.SetStatusCode(fasthttp.StatusNotFound) if res != nil { // Update cache if the request is fresh - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), fileResponse{ - exists: false, + _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ + Exists: false, }, fileCacheTimeout) } return false } if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { - fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", req.RequestURI(), err, res.StatusCode()) + fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", uri, err, res.StatusCode()) html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } @@ -158,15 +138,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st log.Debug().Msg("error handling") // Set the MIME type - mimeType := mime.TypeByExtension(path.Ext(o.TargetPath)) - mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if _, ok := o.ForbiddenMimeTypes[mimeTypeSplit[0]]; ok || mimeType == "" { - if o.DefaultMimeType != "" { - mimeType = o.DefaultMimeType - } else { - mimeType = "application/octet-stream" - } - } + mimeType := o.getMimeTypeByExtension() ctx.Response.Header.SetContentType(mimeType) // Everything's okay so far @@ -185,20 +157,20 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaRoot, giteaAPIToken st err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) } } else { - _, err = ctx.Write(cachedResponse.body) + _, err = ctx.Write(cachedResponse.Body) } if err != nil { - fmt.Printf("Couldn't write body for \"%s\": %s\n", req.RequestURI(), err) + fmt.Printf("Couldn't write body for \"%s\": %s\n", uri, err) html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } log.Debug().Msg("response") if res != nil && ctx.Err() == nil { - cachedResponse.exists = true - cachedResponse.mimeType = mimeType - cachedResponse.body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+strconv.FormatInt(o.BranchTimestamp.Unix(), 10), cachedResponse, fileCacheTimeout) + cachedResponse.Exists = true + cachedResponse.MimeType = mimeType + cachedResponse.Body = cacheBodyWriter.Bytes() + _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), cachedResponse, fileCacheTimeout) } return true From 35b35c5d67c83da87b6f4eeac5e6bd6242f2d807 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Sat, 11 Jun 2022 23:17:43 +0200 Subject: [PATCH 119/320] Add integration tests (#86) close #82 close #32 make sure we dont get regressions again ... as we currently have in **main** followups: - create a DNS subdomayn specific to redirect to mock url ... Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/86 Reviewed-by: crapStone --- .woodpecker.yml | 17 ++++++++-- Justfile | 2 +- go.mod | 1 + go.sum | 2 ++ integration/get_test.go | 69 ++++++++++++++++++++++++++++++++++++++++ integration/main_test.go | 62 ++++++++++++++++++++++++++++++++++++ main.go | 1 + 7 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 integration/get_test.go create mode 100644 integration/main_test.go diff --git a/.woodpecker.yml b/.woodpecker.yml index 271aaca..e7fa66a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -8,19 +8,30 @@ pipeline: - go mod vendor lint: - image: golangci/golangci-lint:v1.45.2 + image: golangci/golangci-lint:latest + pull: true commands: - go version - go install mvdan.cc/gofumpt@latest - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - - golangci-lint run --timeout 5m + - golangci-lint run --timeout 5m --build-tags integration test: image: golang:1.18 commands: - - go test ./... + - go test -race codeberg.org/codeberg/pages/server/... build: image: golang:1.18 commands: - go build + + integration-tests: + image: golang:1.18 + commands: + - go test -race -tags integration codeberg.org/codeberg/pages/integration/... + environment: + - ACME_API=https://acme.mock.directory + - PAGES_DOMAIN=localhost.mock.directory + - RAW_DOMAIN=raw.localhost.mock.directory + - PORT=4430 \ No newline at end of file diff --git a/Justfile b/Justfile index bab0a1e..c5c5a08 100644 --- a/Justfile +++ b/Justfile @@ -13,7 +13,7 @@ build: lint: tool-golangci tool-gofumpt [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ - golangci-lint run --timeout 5m + golangci-lint run --timeout 5m --build-tags integration fmt: tool-gofumpt gofumpt -w --extra . diff --git a/go.mod b/go.mod index 38a289a..13df431 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,7 @@ require ( github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/jarcoal/httpmock v1.0.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/joho/godotenv v1.4.0 // indirect github.com/json-iterator/go v1.1.7 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/klauspost/compress v1.13.4 // indirect diff --git a/go.sum b/go.sum index a443641..f009f5e 100644 --- a/go.sum +++ b/go.sum @@ -266,6 +266,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= diff --git a/integration/get_test.go b/integration/get_test.go new file mode 100644 index 0000000..3f2048e --- /dev/null +++ b/integration/get_test.go @@ -0,0 +1,69 @@ +//go:build integration +// +build integration + +package integration + +import ( + "bytes" + "crypto/tls" + "io" + "net/http" + "net/http/cookiejar" + "testing" + + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" +) + +func TestGetRedirect(t *testing.T) { + log.Printf("== TestGetRedirect ==\n") + // test custom domain redirect + resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "https://www.cabr2.de/", resp.Header["Location"][0]) + assert.EqualValues(t, 0, getSize(resp.Body)) +} + +func TestGetContent(t *testing.T) { + log.Printf("== TestGetContent ==\n") + // test get image + resp, err := getTestHTTPSClient().Get("https://magiclike.localhost.mock.directory:4430/images/827679288a.jpg") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "image/jpeg", resp.Header["Content-Type"][0]) + assert.EqualValues(t, "124635", resp.Header["Content-Length"][0]) + assert.EqualValues(t, 124635, getSize(resp.Body)) + + // specify branch + resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0]) + assert.True(t, getSize(resp.Body) > 1000) +} + +func getTestHTTPSClient() *http.Client { + cookieJar, _ := cookiejar.New(nil) + return &http.Client{ + Jar: cookieJar, + CheckRedirect: func(_ *http.Request, _ []*http.Request) error { + return http.ErrUseLastResponse + }, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } +} + +func getSize(stream io.Reader) int { + buf := new(bytes.Buffer) + _, _ = buf.ReadFrom(stream) + return buf.Len() +} diff --git a/integration/main_test.go b/integration/main_test.go new file mode 100644 index 0000000..43f6792 --- /dev/null +++ b/integration/main_test.go @@ -0,0 +1,62 @@ +//go:build integration +// +build integration + +package integration + +import ( + "context" + "os" + "testing" + "time" + + "codeberg.org/codeberg/pages/cmd" + + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" +) + +func TestMain(m *testing.M) { + log.Printf("=== TestMain: START Server ==\n") + serverCtx, serverCancel := context.WithCancel(context.Background()) + if err := startServer(serverCtx); err != nil { + log.Fatal().Msgf("could not start server: %v", err) + } + defer func() { + serverCancel() + log.Printf("=== TestMain: Server STOPED ==\n") + }() + + time.Sleep(20 * time.Second) + + os.Exit(m.Run()) +} + +func startServer(ctx context.Context) error { + args := []string{ + "--verbose", + "--acme-accept-terms", "true", + } + setEnvIfNotSet("ACME_API", "https://acme.mock.directory") + setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") + setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") + setEnvIfNotSet("PORT", "4430") + + app := cli.NewApp() + app.Name = "pages-server" + app.Action = cmd.Serve + app.Flags = cmd.ServeFlags + + go func() { + if err := app.RunContext(ctx, args); err != nil { + log.Fatal().Msgf("run server error: %v", err) + } + }() + + return nil +} + +func setEnvIfNotSet(key, value string) { + if _, set := os.LookupEnv(key); !set { + os.Setenv(key, value) + } +} diff --git a/main.go b/main.go index 205ec93..2836b86 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + _ "github.com/joho/godotenv/autoload" "github.com/urfave/cli/v2" "codeberg.org/codeberg/pages/cmd" From 38fb28f84f5a2a9b3f993ef6b178472187655aa1 Mon Sep 17 00:00:00 2001 From: crystal Date: Sun, 12 Jun 2022 03:50:00 +0200 Subject: [PATCH 120/320] implement custom 404 pages (#81) solves #56. - The expected filename is `404.html`, like GitHub Pages - Each repo/branch can have one `404.html` file at it's root - If a repo does not have a `pages` branch, the 404.html file from the `pages` repository is used - You get status code 404 (unless you request /404.html which returns 200) - The error page is cached --- close #56 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/81 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: crystal Co-committed-by: crystal --- integration/get_test.go | 13 +++++++++++++ server/upstream/upstream.go | 26 ++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 3f2048e..ef00531 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -49,6 +49,19 @@ func TestGetContent(t *testing.T) { assert.True(t, getSize(resp.Body) > 1000) } +func TestGetNotFound(t *testing.T) { + log.Printf("== TestGetNotFound ==\n") + // test custom not found pages + resp, err := getTestHTTPSClient().Get("https://crystal.localhost.mock.directory:4430/pages-404-demo/blah") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusNotFound, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0]) + assert.EqualValues(t, "37", resp.Header["Content-Length"][0]) + assert.EqualValues(t, 37, getSize(resp.Body)) +} + func getTestHTTPSClient() *http.Client { cookieJar, _ := cookiejar.New(nil) return &http.Client{ diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index edf4f3f..da97021 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -21,6 +21,11 @@ var upstreamIndexPages = []string{ "index.html", } +// upstreamNotFoundPages lists pages that may be considered as custom 404 Not Found pages. +var upstreamNotFoundPages = []string{ + "404.html", +} + // Options provides various options for the upstream request. type Options struct { TargetOwner, @@ -107,6 +112,21 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } } ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + if o.TryIndexPages { + // copy the o struct & try if a not found page exists + optionsForNotFoundPages := *o + optionsForNotFoundPages.TryIndexPages = false + optionsForNotFoundPages.appendTrailingSlash = false + for _, notFoundPage := range upstreamNotFoundPages { + optionsForNotFoundPages.TargetPath = "/" + notFoundPage + if optionsForNotFoundPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { + _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ + Exists: false, + }, fileCacheTimeout) + return true + } + } + } if res != nil { // Update cache if the request is fresh _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ @@ -141,8 +161,10 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, mimeType := o.getMimeTypeByExtension() ctx.Response.Header.SetContentType(mimeType) - // Everything's okay so far - ctx.Response.SetStatusCode(fasthttp.StatusOK) + if ctx.Response.StatusCode() != fasthttp.StatusNotFound { + // Everything's okay so far + ctx.Response.SetStatusCode(fasthttp.StatusOK) + } ctx.Response.Header.SetLastModified(o.BranchTimestamp) log.Debug().Msg("response preparations") From 913f762eb0b4bce42e74b921dd2788ca8c0da7e9 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Mon, 13 Jun 2022 14:43:49 +0200 Subject: [PATCH 121/320] Add integration test for custom domain (#90) and some nits --- close #89 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/90 --- Justfile | 6 ++++++ integration/get_test.go | 18 +++++++++++++++--- integration/main_test.go | 4 ++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Justfile b/Justfile index c5c5a08..720c0f1 100644 --- a/Justfile +++ b/Justfile @@ -27,3 +27,9 @@ tool-gofumpt: @hash gofumpt> /dev/null 2>&1; if [ $? -ne 0 ]; then \ go install mvdan.cc/gofumpt@latest; \ fi + +test: + go test -race codeberg.org/codeberg/pages/server/... + +integration: + go test -race -tags integration codeberg.org/codeberg/pages/integration/... diff --git a/integration/get_test.go b/integration/get_test.go index ef00531..973b049 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -16,7 +16,7 @@ import ( ) func TestGetRedirect(t *testing.T) { - log.Printf("== TestGetRedirect ==\n") + log.Printf("=== TestGetRedirect ===\n") // test custom domain redirect resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430") assert.NoError(t, err) @@ -28,7 +28,7 @@ func TestGetRedirect(t *testing.T) { } func TestGetContent(t *testing.T) { - log.Printf("== TestGetContent ==\n") + log.Printf("=== TestGetContent ===\n") // test get image resp, err := getTestHTTPSClient().Get("https://magiclike.localhost.mock.directory:4430/images/827679288a.jpg") assert.NoError(t, err) @@ -49,8 +49,20 @@ func TestGetContent(t *testing.T) { assert.True(t, getSize(resp.Body) > 1000) } +func TestCustomDomain(t *testing.T) { + log.Printf("=== TestCustomDomain ===\n") + resp, err := getTestHTTPSClient().Get("https://mock-pages.codeberg-test.org:4430/README.md") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header["Content-Type"][0]) + assert.EqualValues(t, "106", resp.Header["Content-Length"][0]) + assert.EqualValues(t, 106, getSize(resp.Body)) +} + func TestGetNotFound(t *testing.T) { - log.Printf("== TestGetNotFound ==\n") + log.Printf("=== TestGetNotFound ===\n") // test custom not found pages resp, err := getTestHTTPSClient().Get("https://crystal.localhost.mock.directory:4430/pages-404-demo/blah") assert.NoError(t, err) diff --git a/integration/main_test.go b/integration/main_test.go index 43f6792..8104413 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -16,14 +16,14 @@ import ( ) func TestMain(m *testing.M) { - log.Printf("=== TestMain: START Server ==\n") + log.Printf("=== TestMain: START Server ===\n") serverCtx, serverCancel := context.WithCancel(context.Background()) if err := startServer(serverCtx); err != nil { log.Fatal().Msgf("could not start server: %v", err) } defer func() { serverCancel() - log.Printf("=== TestMain: Server STOPED ==\n") + log.Printf("=== TestMain: Server STOPED ===\n") }() time.Sleep(20 * time.Second) From cc32bab31fa54e4614b539a712fa015dd8af4bfd Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Mon, 13 Jun 2022 20:07:32 +0200 Subject: [PATCH 122/320] Enhance joinURL and return error on gitea client on start instead while running (#88) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/88 --- cmd/main.go | 5 ++++- server/gitea/client.go | 23 +++++++++++++++++++---- server/gitea/client_test.go | 22 +++++++++++----------- server/handler_test.go | 2 +- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 257b724..80fb666 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -82,7 +82,10 @@ func Serve(ctx *cli.Context) error { // TODO: make this an MRU cache with a size limit fileResponseCache := cache.NewKeyValueCache() - giteaClient := gitea.NewClient(giteaRoot, giteaAPIToken) + giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken) + if err != nil { + return fmt.Errorf("could not create new gitea client: %v", err) + } // Create handler based on settings handler := server.Handler(mainDomainSuffix, []byte(rawDomain), diff --git a/server/gitea/client.go b/server/gitea/client.go index d4eb980..5410413 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" "net/url" - "path" + "strings" "time" "github.com/valyala/fasthttp" @@ -29,18 +29,33 @@ type FileResponse struct { Body []byte } -func joinURL(giteaRoot string, paths ...string) string { return giteaRoot + path.Join(paths...) } +// TODO: once golang v1.19 is min requirement, we can switch to 'JoinPath()' of 'net/url' package +func joinURL(baseURL string, paths ...string) string { + p := make([]string, 0, len(paths)) + for i := range paths { + path := strings.TrimSpace(paths[i]) + path = strings.Trim(path, "/") + if len(path) != 0 { + p = append(p, path) + } + } + + return baseURL + "/" + strings.Join(p, "/") +} func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } -func NewClient(giteaRoot, giteaAPIToken string) *Client { +func NewClient(giteaRoot, giteaAPIToken string) (*Client, error) { + rootURL, err := url.Parse(giteaRoot) + giteaRoot = strings.Trim(rootURL.String(), "/") + return &Client{ giteaRoot: giteaRoot, giteaAPIToken: giteaAPIToken, infoTimeout: 5 * time.Second, contentTimeout: 10 * time.Second, fastClient: getFastHTTPClient(), - } + }, err } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { diff --git a/server/gitea/client_test.go b/server/gitea/client_test.go index bae9d4e..7dbad68 100644 --- a/server/gitea/client_test.go +++ b/server/gitea/client_test.go @@ -1,23 +1,23 @@ package gitea import ( + "net/url" "testing" "github.com/stretchr/testify/assert" ) func TestJoinURL(t *testing.T) { - url := joinURL("") - assert.EqualValues(t, "", url) + baseURL := "" + assert.EqualValues(t, "/", joinURL(baseURL)) + assert.EqualValues(t, "/", joinURL(baseURL, "", "")) - url = joinURL("", "", "") - assert.EqualValues(t, "", url) + baseURL = "http://wwow.url.com" + assert.EqualValues(t, "http://wwow.url.com/a/b/c/d", joinURL(baseURL, "a", "b/c/", "d")) - url = joinURL("http://wwow.url.com", "a", "b/c/", "d") - // assert.EqualValues(t, "http://wwow.url.com/a/b/c/d", url) - assert.EqualValues(t, "http://wwow.url.coma/b/c/d", url) - - url = joinURL("h:://wrong", "acdc") - // assert.EqualValues(t, "h:://wrong/acdc", url) - assert.EqualValues(t, "h:://wrongacdc", url) + baseURL = "http://wow.url.com/subpath/2" + assert.EqualValues(t, "http://wow.url.com/subpath/2/content.pdf", joinURL(baseURL, "/content.pdf")) + assert.EqualValues(t, "http://wow.url.com/subpath/2/wonderful.jpg", joinURL(baseURL, "wonderful.jpg")) + assert.EqualValues(t, "http://wow.url.com/subpath/2/raw/wonderful.jpg?ref=main", joinURL(baseURL, "raw", "wonderful.jpg"+"?ref="+url.QueryEscape("main"))) + assert.EqualValues(t, "http://wow.url.com/subpath/2/raw/wonderful.jpg%3Fref=main", joinURL(baseURL, "raw", "wonderful.jpg%3Fref=main")) } diff --git a/server/handler_test.go b/server/handler_test.go index 73002a2..23d9af5 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -13,7 +13,7 @@ import ( func TestHandlerPerformance(t *testing.T) { giteaRoot := "https://codeberg.org" - giteaClient := gitea.NewClient(giteaRoot, "") + giteaClient, _ := gitea.NewClient(giteaRoot, "") testHandler := Handler( []byte("codeberg.page"), []byte("raw.codeberg.org"), giteaClient, From 4c6164ef05d6f6ffe45a50527e24423776b49421 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Tue, 14 Jun 2022 18:23:34 +0200 Subject: [PATCH 123/320] Propagate ETag from gitea (#93) close #15 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/93 --- integration/get_test.go | 18 ++++++++++-------- server/gitea/client.go | 1 + server/upstream/const.go | 1 + server/upstream/upstream.go | 8 ++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 973b049..9cb66f5 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -23,7 +23,7 @@ func TestGetRedirect(t *testing.T) { if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) { t.FailNow() } - assert.EqualValues(t, "https://www.cabr2.de/", resp.Header["Location"][0]) + assert.EqualValues(t, "https://www.cabr2.de/", resp.Header.Get("Location")) assert.EqualValues(t, 0, getSize(resp.Body)) } @@ -35,9 +35,10 @@ func TestGetContent(t *testing.T) { if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { t.FailNow() } - assert.EqualValues(t, "image/jpeg", resp.Header["Content-Type"][0]) - assert.EqualValues(t, "124635", resp.Header["Content-Length"][0]) + assert.EqualValues(t, "image/jpeg", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "124635", resp.Header.Get("Content-Length")) assert.EqualValues(t, 124635, getSize(resp.Body)) + assert.Len(t, resp.Header.Get("ETag"), 42) // specify branch resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/") @@ -45,8 +46,9 @@ func TestGetContent(t *testing.T) { if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { t.FailNow() } - assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0]) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.True(t, getSize(resp.Body) > 1000) + assert.Len(t, resp.Header.Get("ETag"), 42) } func TestCustomDomain(t *testing.T) { @@ -56,8 +58,8 @@ func TestCustomDomain(t *testing.T) { if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { t.FailNow() } - assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header["Content-Type"][0]) - assert.EqualValues(t, "106", resp.Header["Content-Length"][0]) + assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "106", resp.Header.Get("Content-Length")) assert.EqualValues(t, 106, getSize(resp.Body)) } @@ -69,8 +71,8 @@ func TestGetNotFound(t *testing.T) { if !assert.EqualValues(t, http.StatusNotFound, resp.StatusCode) { t.FailNow() } - assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0]) - assert.EqualValues(t, "37", resp.Header["Content-Length"][0]) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "37", resp.Header.Get("Content-Length")) assert.EqualValues(t, 37, getSize(resp.Body)) } diff --git a/server/gitea/client.go b/server/gitea/client.go index 5410413..7b5d009 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -25,6 +25,7 @@ type Client struct { type FileResponse struct { Exists bool + ETag []byte MimeType string Body []byte } diff --git a/server/upstream/const.go b/server/upstream/const.go index ce76e21..247e1d1 100644 --- a/server/upstream/const.go +++ b/server/upstream/const.go @@ -12,6 +12,7 @@ var branchExistenceCacheTimeout = 5 * time.Minute // fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending // on your available memory. +// TODO: move as option into cache interface var fileCacheTimeout = 5 * time.Minute // fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index da97021..7c9a035 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -161,6 +161,14 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, mimeType := o.getMimeTypeByExtension() ctx.Response.Header.SetContentType(mimeType) + // Set ETag + if cachedResponse.Exists { + ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag) + } else if res != nil { + cachedResponse.ETag = res.Header.Peek(fasthttp.HeaderETag) + ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag) + } + if ctx.Response.StatusCode() != fasthttp.StatusNotFound { // Everything's okay so far ctx.Response.SetStatusCode(fasthttp.StatusOK) From 6dedd55eb38139742ffac62f3323355f1feb5068 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Tue, 14 Jun 2022 20:35:11 +0200 Subject: [PATCH 124/320] Release via CI (#94) * release via CI * general CI improvements close #76, close #92 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/94 --- .woodpecker.yml | 52 +++++++++++++++++++++++++++++++-------- Justfile | 9 +++++++ integration/main_test.go | 2 +- server/handler.go | 3 ++- server/version/version.go | 3 +++ 5 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 server/version/version.go diff --git a/.woodpecker.yml b/.woodpecker.yml index e7fa66a..081d131 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -9,6 +9,7 @@ pipeline: lint: image: golangci/golangci-lint:latest + group: compliant pull: true commands: - go version @@ -16,22 +17,53 @@ pipeline: - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - golangci-lint run --timeout 5m --build-tags integration - test: - image: golang:1.18 - commands: - - go test -race codeberg.org/codeberg/pages/server/... - build: - image: golang:1.18 + group: compliant + image: a6543/golang_just commands: - - go build + - go version + - just build + when: + event: [ "pull_request", "push" ] + + build-tag: + group: compliant + image: a6543/golang_just + commands: + - go version + - just build-tag ${CI_COMMIT_TAG##v} + when: + event: [ "tag" ] + + test: + image: a6543/golang_just + group: test + commands: + - just test integration-tests: - image: golang:1.18 + image: a6543/golang_just + group: test commands: - - go test -race -tags integration codeberg.org/codeberg/pages/integration/... + - just integration environment: - ACME_API=https://acme.mock.directory - PAGES_DOMAIN=localhost.mock.directory - RAW_DOMAIN=raw.localhost.mock.directory - - PORT=4430 \ No newline at end of file + - PORT=4430 + + release: + image: plugins/gitea-release + settings: + base_url: https://codeberg.org + file_exists: overwrite + files: build/codeberg-pages-server + api_key: + from_secret: bot_token + environment: + - DRONE_REPO_OWNER=${CI_REPO_OWNER} + - DRONE_REPO_NAME=${CI_REPO_NAME} + - DRONE_BUILD_EVENT=${CI_BUILD_EVENT} + - DRONE_COMMIT_REF=${CI_COMMIT_REF} + when: + event: [ "tag" ] diff --git a/Justfile b/Justfile index 720c0f1..a908e7a 100644 --- a/Justfile +++ b/Justfile @@ -11,6 +11,9 @@ dev: build: CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ +build-tag VERSION: + CGO_ENABLED=0 go build -ldflags '-s -w -X "codeberg.org/codeberg/pages/server/version.Version={{VERSION}}"' -v -o build/codeberg-pages-server ./ + lint: tool-golangci tool-gofumpt [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ golangci-lint run --timeout 5m --build-tags integration @@ -31,5 +34,11 @@ tool-gofumpt: test: go test -race codeberg.org/codeberg/pages/server/... +test-run TEST: + go test -race -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... + integration: go test -race -tags integration codeberg.org/codeberg/pages/integration/... + +integration-run TEST: + go test -race -tags integration -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... \ No newline at end of file diff --git a/integration/main_test.go b/integration/main_test.go index 8104413..c8f524f 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -26,7 +26,7 @@ func TestMain(m *testing.M) { log.Printf("=== TestMain: Server STOPED ===\n") }() - time.Sleep(20 * time.Second) + time.Sleep(10 * time.Second) os.Exit(m.Run()) } diff --git a/server/handler.go b/server/handler.go index bda7bd0..3a2667d 100644 --- a/server/handler.go +++ b/server/handler.go @@ -14,6 +14,7 @@ import ( "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" "codeberg.org/codeberg/pages/server/utils" + "codeberg.org/codeberg/pages/server/version" ) // Handler handles a single HTTP request to the web server. @@ -26,7 +27,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, return func(ctx *fasthttp.RequestCtx) { log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() - ctx.Response.Header.Set("Server", "Codeberg Pages") + ctx.Response.Header.Set("Server", "CodebergPages/"+version.Version) // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") diff --git a/server/version/version.go b/server/version/version.go new file mode 100644 index 0000000..aa2cbb5 --- /dev/null +++ b/server/version/version.go @@ -0,0 +1,3 @@ +package version + +var Version string = "dev" From 48a49f69a7dfe8faa584285a3f5fc0fa48fbd133 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 3 Jul 2022 13:20:02 +0200 Subject: [PATCH 125/320] Increase concurrent connections to default value (#99) Use the default value of `256 * 1024` for the concurrency limit, this will mean that the server will be able to handle more connections. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/99 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- server/setup.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/setup.go b/server/setup.go index c186222..027b3e1 100644 --- a/server/setup.go +++ b/server/setup.go @@ -21,7 +21,6 @@ func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { NoDefaultServerHeader: true, NoDefaultDate: true, ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge - Concurrency: 1024 * 32, // TODO: adjust bottlenecks for best performance with Gitea! } } From 9076bc3f75e05af80d2b812c7c1f72b69e1ce86f Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Fri, 8 Jul 2022 13:39:24 +0200 Subject: [PATCH 126/320] Support access branch that contains slash character (#102) So we can access branch that contain slash like `branch/name` with `username.codeberg.page/repo/@branch~name/`. Branch name cannot contain `~` character but it can be in a HTTP URL, so replace the `~` from URL to `/` could be a valid solution to me. Resolve #101 Co-authored-by: Gary Wang Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/102 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gary Wang Co-committed-by: Gary Wang --- README.md | 3 ++- integration/get_test.go | 10 ++++++++++ server/handler.go | 4 ++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6543c90..700f279 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Mapping custom domains is not static anymore, but can be done with DNS: first line will be the canonical domain/URL; all other occurrences will be redirected to it. 2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to -"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else): +"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else. +If the branch name contains slash characters, you need to replace "/" in the branch name to "~"): `www.example.org. IN CNAME main.pages.example.codeberg.page.` 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record diff --git a/integration/get_test.go b/integration/get_test.go index 9cb66f5..1997010 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -49,6 +49,16 @@ func TestGetContent(t *testing.T) { assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.True(t, getSize(resp.Body) > 1000) assert.Len(t, resp.Header.Get("ETag"), 42) + + // access branch name contains '/' + resp, err = getTestHTTPSClient().Get("https://blumia.localhost.mock.directory:4430/pages-server-integration-tests/@docs~main/") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + assert.True(t, getSize(resp.Body) > 100) + assert.Len(t, resp.Header.Get("ETag"), 42) } func TestCustomDomain(t *testing.T) { diff --git a/server/handler.go b/server/handler.go index 3a2667d..11da0a5 100644 --- a/server/handler.go +++ b/server/handler.go @@ -89,6 +89,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, return false } + // Replace "~" to "/" so we can access branch that contains slash character + // Branch name cannot contain "~" so doing this is okay + branch = strings.ReplaceAll(branch, "~", "/") + // Check if the branch exists, otherwise treat it as a file path branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch, branchTimestampCache) if branchTimestampResult == nil { From fd24b4a2bc7f6210bc18da09903accd2235697c2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 12 Jul 2022 15:32:48 +0200 Subject: [PATCH 127/320] Pass logger to fasthttp (#98) - Use a logger with `FASTHTTP` prefix as fasthttp's logger so it's easy to see what fasthttp is logging in console/journal. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/98 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- server/setup.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/setup.go b/server/setup.go index 027b3e1..ea96d1e 100644 --- a/server/setup.go +++ b/server/setup.go @@ -5,12 +5,20 @@ import ( "net/http" "time" + "github.com/rs/zerolog/log" + "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/utils" ) +type fasthttpLogger struct{} + +func (fasthttpLogger) Printf(format string, args ...interface{}) { + log.Printf("[FASTHTTP] "+format, args...) +} + func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { // Enable compression by wrapping the handler with the compression function provided by FastHTTP compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) @@ -21,6 +29,7 @@ func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { NoDefaultServerHeader: true, NoDefaultDate: true, ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge + Logger: fasthttpLogger{}, } } From baf4e7e326baea1a08e539d89f7c73c2980152ee Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 15 Jul 2022 17:18:25 +0200 Subject: [PATCH 128/320] Make the 404 page more readable and natural (#104) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/104 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Jeremy Co-committed-by: Jeremy --- html/404.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/html/404.html b/html/404.html index 08ab16d..b7ec96e 100644 --- a/html/404.html +++ b/html/404.html @@ -23,11 +23,11 @@

- This page was not found! + Page not found!

- Sorry, this page doesn't exist or is inaccessible for other reasons (%status).
- We hope this is not our fault ;) - Make sure to check the troubleshooting section in the Docs! + Sorry, but this page couldn't be found or is inaccessible (%status).
+ We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs!
From 5411c96ef391e84cef358258272bf0e6fd406f59 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 15 Jul 2022 21:06:05 +0200 Subject: [PATCH 129/320] Tell fasthttp to not set "Content-Length: 0" on non cached content (#107) fix #97 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/107 --- integration/get_test.go | 2 ++ server/gitea/client.go | 1 - server/upstream/upstream.go | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/integration/get_test.go b/integration/get_test.go index 1997010..8af40f0 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -59,6 +59,8 @@ func TestGetContent(t *testing.T) { assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.True(t, getSize(resp.Body) > 100) assert.Len(t, resp.Header.Get("ETag"), 42) + + // TODO: test get of non cachable content (content size > fileCacheSizeLimit) } func TestCustomDomain(t *testing.T) { diff --git a/server/gitea/client.go b/server/gitea/client.go index 7b5d009..3b9ad6f 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -82,7 +82,6 @@ func (client *Client) ServeRawContent(uri string) (*fasthttp.Response, error) { if err != nil { return nil, err } - // resp.SetBodyStream(&strings.Reader{}, -1) if err != nil { return nil, err diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 7c9a035..b9fc2bc 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -181,6 +181,9 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, var cacheBodyWriter bytes.Buffer if res != nil { if res.Header.ContentLength() > fileCacheSizeLimit { + // fasthttp else will set "Content-Length: 0" + ctx.Response.SetBodyStream(&strings.Reader{}, -1) + err = res.BodyWriteTo(ctx.Response.BodyWriter()) } else { // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? From bcaceda71177d701f91c01eb04213e126de71ea0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 15 Jul 2022 21:21:26 +0200 Subject: [PATCH 130/320] dont cache if ContentLength greater fileCacheSizeLimit (#108) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/108 Reviewed-by: Otto --- Justfile | 4 ++++ server/upstream/upstream.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index a908e7a..c824efa 100644 --- a/Justfile +++ b/Justfile @@ -21,6 +21,10 @@ lint: tool-golangci tool-gofumpt fmt: tool-gofumpt gofumpt -w --extra . +clean: + go clean ./... + rm -rf build/ + tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest; \ diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index b9fc2bc..5e888ac 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -199,7 +199,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } log.Debug().Msg("response") - if res != nil && ctx.Err() == nil { + if res != nil && res.Header.ContentLength() > fileCacheSizeLimit && ctx.Err() == nil { cachedResponse.Exists = true cachedResponse.MimeType = mimeType cachedResponse.Body = cacheBodyWriter.Bytes() From 8207586a4818b011649dbae8ca646196a3d74b21 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 15 Jul 2022 21:39:42 +0200 Subject: [PATCH 131/320] just fix bcaceda71177d701f91c01eb04213e126de71ea0 --- server/upstream/upstream.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 5e888ac..9b7464e 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -199,7 +199,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } log.Debug().Msg("response") - if res != nil && res.Header.ContentLength() > fileCacheSizeLimit && ctx.Err() == nil { + if res != nil && res.Header.ContentLength() <= fileCacheSizeLimit && ctx.Err() == nil { cachedResponse.Exists = true cachedResponse.MimeType = mimeType cachedResponse.Body = cacheBodyWriter.Bytes() From 7d57c0a8a86751ce612ba721a825a8a5e95917a3 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 15 Jul 2022 23:34:09 +0200 Subject: [PATCH 132/320] update dependencies --- Cargo.lock | 105 +++++++++++++++++++++++++++++++++++++---------------- Cargo.toml | 9 +++-- build.rs | 29 ++++++++++----- src/cli.rs | 18 ++++----- 4 files changed, 106 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c0fe92..0141983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "atty" version = "0.2.14" @@ -22,6 +13,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bitflags" version = "1.3.2" @@ -30,17 +27,35 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "clap" -version = "2.33.3" +version = "3.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_lex", + "indexmap", "strsim", + "termcolor", "textwrap", - "unicode-width", - "vec_map", +] + +[[package]] +name = "clap_complete" +version = "3.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead064480dfc4880a10764488415a97fdd36a4cf1bb022d372f02e8faf8386e1" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -49,6 +64,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" +[[package]] +name = "hashbrown" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -58,46 +79,57 @@ dependencies = [ "libc", ] +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "lamp" -version = "0.2.0" +version = "0.2.1" dependencies = [ "clap", + "clap_complete", "exitcode", ] [[package]] name = "libc" -version = "0.2.105" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "os_str_bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "textwrap" -version = "0.11.0" +name = "termcolor" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "unicode-width", + "winapi-util", ] [[package]] -name = "unicode-width" -version = "0.1.9" +name = "textwrap" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "winapi" @@ -115,6 +147,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 9666175..8235fa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lamp" -version = "0.2.0" +version = "0.2.1" authors = ["crapStone "] edition = "2021" @@ -9,14 +9,15 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] -clap = "2.33.3" +clap = "3.2.12" +clap_complete = "3.2.3" [dependencies] -clap = "2.33.3" +clap = "3.2.12" exitcode = "1.1.2" [profile.release] lto = true panic = "abort" +strip = true codegen-units = 1 - diff --git a/build.rs b/build.rs index 4365679..bc3d6ea 100644 --- a/build.rs +++ b/build.rs @@ -1,14 +1,23 @@ -use clap::Shell; +use std::{path::PathBuf, io::Error}; + +use clap_complete::{generate_to, shells, Generator}; include!("src/cli.rs"); -fn main() { - let outdir = "completions"; // match env::var_os("OUT_DIR") { - // None => return, - // Some(outdir) => outdir, - // }; - let mut app = build_cli(); - app.gen_completions("lamp", Shell::Fish, outdir); - // app.gen_completions("lamp", Shell::Zsh, outdir); // TODO search for bug - app.gen_completions("lamp", Shell::Bash, outdir); +fn main() -> Result<(), Error> { + let outdir = "completions"; + let mut cmd = build_cli(); + + let path = generate_completions(shells::Bash, &mut cmd, outdir)?; + println!("cargo:debug=completion file is generated: {:?}", path); + let path = generate_completions(shells::Zsh, &mut cmd, outdir)?; + println!("cargo:debug=completion file is generated: {:?}", path); + let path = generate_completions(shells::Fish, &mut cmd, outdir)?; + println!("cargo:debug=completion file is generated: {:?}", path); + + Ok(()) +} + +fn generate_completions(shell: G, cmd: &mut clap::Command, outdir: &str) -> Result { + generate_to(shell, cmd, "lamp", outdir) } diff --git a/src/cli.rs b/src/cli.rs index 213c47c..69410f7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,6 @@ use clap::{App, Arg, ArgGroup}; -pub fn build_cli() -> App<'static, 'static> { +pub fn build_cli() -> App<'static> { App::new("lamp") .version(env!("CARGO_PKG_VERSION")) .author("crapStone ") @@ -8,7 +8,7 @@ pub fn build_cli() -> App<'static, 'static> { .global_setting(clap::AppSettings::ArgRequiredElseHelp) .arg( Arg::with_name("set") - .short("s") + .short('s') .long("set") .value_name("VALUE") .help("Sets brightness to given value") @@ -16,7 +16,7 @@ pub fn build_cli() -> App<'static, 'static> { ) .arg( Arg::with_name("inc") - .short("i") + .short('i') .long("increase") .value_name("PERCENT") .help("Increases brightness") @@ -24,7 +24,7 @@ pub fn build_cli() -> App<'static, 'static> { ) .arg( Arg::with_name("dec") - .short("d") + .short('d') .long("decrease") .value_name("PERCENT") .help("Decreases brightness") @@ -32,19 +32,19 @@ pub fn build_cli() -> App<'static, 'static> { ) .arg( Arg::with_name("get") - .short("g") + .short('g') .long("get") .help("Prints current brightness value"), ) .arg( Arg::with_name("zer") - .short("z") + .short('z') .long("zero") .help("Sets brightness to lowest value"), ) .arg( Arg::with_name("ful") - .short("f") + .short('f') .long("full") .help("Sets brightness to highest value"), ) @@ -54,14 +54,14 @@ pub fn build_cli() -> App<'static, 'static> { ) .arg( Arg::with_name("list") - .short("l") + .short('l') .long("list") .help("Lists all available brightness and led controllers") .conflicts_with_all(&["brightness_control"]), ) .arg( Arg::with_name("ctrl_type") - .short("t") + .short('t') .long("type") .value_name("controller_type") .takes_value(true) From 114e56782695c051c73bb6471d485535de184dff Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 15 Jul 2022 23:55:31 +0200 Subject: [PATCH 133/320] change license to GPLv3 --- Cargo.toml | 5 + LICENSE | 687 +++++++++++++++++++++++++++++++++++++++++++-- build.rs | 5 + src/cli.rs | 5 + src/controllers.rs | 5 + src/main.rs | 5 + 6 files changed, 695 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8235fa7..16a773b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ +# This file is part of the "lamp" program. +# Copyright (C) 2022 crapStone +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + [package] name = "lamp" version = "0.2.1" diff --git a/LICENSE b/LICENSE index f69e94a..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,674 @@ -MIT License + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2021 crapStone + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Preamble -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/build.rs b/build.rs index bc3d6ea..47e69e0 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,8 @@ +// This file is part of the "lamp" program. +// Copyright (C) 2022 crapStone +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::{path::PathBuf, io::Error}; use clap_complete::{generate_to, shells, Generator}; diff --git a/src/cli.rs b/src/cli.rs index 69410f7..7449fd4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,8 @@ +// This file is part of the "lamp" program. +// Copyright (C) 2022 crapStone +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use clap::{App, Arg, ArgGroup}; pub fn build_cli() -> App<'static> { diff --git a/src/controllers.rs b/src/controllers.rs index 8626c21..a4d4923 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -1,3 +1,8 @@ +// This file is part of the "lamp" program. +// Copyright (C) 2022 crapStone +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use std::collections::HashMap; use std::fs::{File, OpenOptions}; use std::io::prelude::*; diff --git a/src/main.rs b/src/main.rs index 8059fbc..41254d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,8 @@ +// This file is part of the "lamp" program. +// Copyright (C) 2022 crapStone +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + mod cli; mod controllers; From 00e8a41c89486e8db3f49cbae7856471e9282f5a Mon Sep 17 00:00:00 2001 From: dorianim Date: Sat, 16 Jul 2022 00:59:55 +0200 Subject: [PATCH 134/320] Add Dockerfile (#111) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/111 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: dorianim Co-committed-by: dorianim --- .woodpecker.yml | 11 +++++++++++ Dockerfile | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 Dockerfile diff --git a/.woodpecker.yml b/.woodpecker.yml index 081d131..b83a4dd 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -26,6 +26,17 @@ pipeline: when: event: [ "pull_request", "push" ] + build-docker: + image: plugins/kaniko + settings: + dockerfile: Dockerfile + no_push: true + repo: Codeberg/pages-server + tags: latest + when: + event: [ "pull_request", "push" ] + path: Dockerfile + build-tag: group: compliant image: a6543/golang_just diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..71dd236 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:alpine as build + +WORKDIR /workspace + +RUN apk add ca-certificates +COPY . . +RUN CGO_ENABLED=0 go build . + +FROM scratch +COPY --from=build /workspace/pages /pages +COPY --from=build \ + /etc/ssl/certs/ca-certificates.crt \ + /etc/ssl/certs/ca-certificates.crt + +ENTRYPOINT ["/pages"] \ No newline at end of file From 3a8ebca75e585f1a1770b1d4cafc9e70f6816824 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 01:01:48 +0200 Subject: [PATCH 135/320] improvements --- build.rs | 32 ++++++++++++++------- src/cli.rs | 4 +-- src/controllers.rs | 69 ++++++++++++++++++++-------------------------- src/main.rs | 10 +++---- 4 files changed, 59 insertions(+), 56 deletions(-) diff --git a/build.rs b/build.rs index 47e69e0..8318948 100644 --- a/build.rs +++ b/build.rs @@ -3,26 +3,38 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::{path::PathBuf, io::Error}; +use std::{ + fs, + io::{self, Error}, + path::PathBuf, +}; use clap_complete::{generate_to, shells, Generator}; include!("src/cli.rs"); +const OUTDIR: &str = "completions"; + fn main() -> Result<(), Error> { - let outdir = "completions"; let mut cmd = build_cli(); - let path = generate_completions(shells::Bash, &mut cmd, outdir)?; - println!("cargo:debug=completion file is generated: {:?}", path); - let path = generate_completions(shells::Zsh, &mut cmd, outdir)?; - println!("cargo:debug=completion file is generated: {:?}", path); - let path = generate_completions(shells::Fish, &mut cmd, outdir)?; - println!("cargo:debug=completion file is generated: {:?}", path); + if let Err(why) = fs::create_dir(OUTDIR) { + if why.kind() != io::ErrorKind::AlreadyExists { + eprintln!("cargo:error=could not create directory: {OUTDIR}"); + return Err(why); + } + } + + let path = generate_completions(shells::Bash, &mut cmd)?; + println!("cargo:debug=completion file is generated: {path:?}"); + let path = generate_completions(shells::Zsh, &mut cmd)?; + println!("cargo:debug=completion file is generated: {path:?}"); + let path = generate_completions(shells::Fish, &mut cmd)?; + println!("cargo:debug=completion file is generated: {path:?}"); Ok(()) } -fn generate_completions(shell: G, cmd: &mut clap::Command, outdir: &str) -> Result { - generate_to(shell, cmd, "lamp", outdir) +fn generate_completions(shell: G, cmd: &mut clap::Command) -> Result { + generate_to(shell, cmd, env!("CARGO_PKG_NAME"), OUTDIR) } diff --git a/src/cli.rs b/src/cli.rs index 7449fd4..d5c97b8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -76,8 +76,8 @@ pub fn build_cli() -> App<'static> { .long_help( r#"You can choose between these controller types: raw: uses the raw values found in the device files -lin: uses percentage values (0.0 - 1.0) with a linear curve for the actual brightness -log: uses percentage values (0.0 - 1.0) with a logarithmic curve for the actual brightness +lin: uses percentage values (0.0 - 1.0) with a linear curve +log: uses percentage values (0.0 - 1.0) with a logarithmic curve the perceived brightness for the human eyes should be linear with this controller "#, ), diff --git a/src/controllers.rs b/src/controllers.rs index a4d4923..cc49b85 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -12,45 +12,39 @@ use std::process::exit; const SYS_PATHS: [&str; 2] = ["/sys/class/backlight", "/sys/class/leds"]; pub trait Controller { - fn get_brightness(&self) -> i32; - fn get_max_brightness(&self) -> i32; - fn set_brightness(&self, value: i32); + fn get_brightness(&self) -> u32; + fn get_max_brightness(&self) -> u32; + fn set_brightness(&self, value: u32); - fn check_brightness_value(&self, value: i32) { - if value > self.get_max_brightness() { - eprintln!( - "brightness value too high: {} > {}", - value, - self.get_max_brightness() - ); - exit(exitcode::DATAERR); - } else if value < 0 { - eprintln!("brightness value too low: {}", value); + fn check_brightness_value(&self, value: u32) { + let max = self.get_max_brightness(); + if value > max { + eprintln!("brightness value too high: {value} > {max}",); exit(exitcode::DATAERR); } } } pub struct RawController { - path: Box, + path: PathBuf, } impl RawController { - pub fn new(path: Box) -> Self { + pub fn new(path: PathBuf) -> Self { Self { path } } } impl Controller for RawController { - fn get_brightness(&self) -> i32 { + fn get_brightness(&self) -> u32 { read_file_to_int(self.path.join("brightness")) } - fn get_max_brightness(&self) -> i32 { + fn get_max_brightness(&self) -> u32 { read_file_to_int(self.path.join("max_brightness")) } - fn set_brightness(&self, value: i32) { + fn set_brightness(&self, value: u32) { self.check_brightness_value(value); let path = self.path.join("brightness"); @@ -67,8 +61,7 @@ impl Controller for RawController { Ok(_) => {} Err(err) => { eprintln!( - "could not write '{}' to file '{}': {:?}", - value, + "could not write '{value}' to file '{}': {:?}", &path.display(), err.kind() ); @@ -83,7 +76,7 @@ pub struct LinController { } impl LinController { - pub fn new(path: Box) -> Self { + pub fn new(path: PathBuf) -> Self { Self { parent_controller: RawController::new(path), } @@ -91,23 +84,22 @@ impl LinController { } impl Controller for LinController { - fn get_brightness(&self) -> i32 { + fn get_brightness(&self) -> u32 { ((self.parent_controller.get_brightness() as f64 / self.parent_controller.get_max_brightness() as f64) - * self.get_max_brightness() as f64) as i32 + * self.get_max_brightness() as f64) as u32 } - fn get_max_brightness(&self) -> i32 { + fn get_max_brightness(&self) -> u32 { 100 } - fn set_brightness(&self, value: i32) { + fn set_brightness(&self, value: u32) { self.check_brightness_value(value); if value > self.get_max_brightness() { eprintln!( - "brightness value too high! {} > {}", - value, + "brightness value too high! {value} > {}", self.get_max_brightness() ); exit(exitcode::DATAERR); @@ -124,7 +116,7 @@ pub struct LogController { } impl LogController { - pub fn new(path: Box) -> Self { + pub fn new(path: PathBuf) -> Self { Self { parent_controller: RawController::new(path), } @@ -132,23 +124,22 @@ impl LogController { } impl Controller for LogController { - fn get_brightness(&self) -> i32 { + fn get_brightness(&self) -> u32 { ((self.parent_controller.get_brightness() as f64).log10() / (self.parent_controller.get_max_brightness() as f64).log10() - * self.get_max_brightness() as f64) as i32 + * self.get_max_brightness() as f64) as u32 } - fn get_max_brightness(&self) -> i32 { + fn get_max_brightness(&self) -> u32 { 100 } - fn set_brightness(&self, value: i32) { + fn set_brightness(&self, value: u32) { self.check_brightness_value(value); if value > self.get_max_brightness() { eprintln!( - "brightness value too high! {} > {}", - value, + "brightness value too high! {value} > {}", self.get_max_brightness() ); exit(exitcode::DATAERR); @@ -157,11 +148,11 @@ impl Controller for LogController { self.parent_controller.set_brightness(10f64.powf( (value as f64 / self.get_max_brightness() as f64) * (self.parent_controller.get_max_brightness() as f64).log10(), - ) as i32) + ) as u32) } } -fn read_file_to_int(path: PathBuf) -> i32 { +fn read_file_to_int(path: PathBuf) -> u32 { let mut file = match File::open(&path) { Err(why) => { eprintln!("couldn't open {}: {:?}", path.display(), why.kind()); @@ -183,8 +174,8 @@ fn read_file_to_int(path: PathBuf) -> i32 { /// Searches through all paths in `SYS_PATHS` and creates a `HashMap` with the name and absolute path. /// /// It returns a `Tuple` of the default backlight name and the `HashMap`. -pub fn get_controllers() -> (String, HashMap>) { - let mut controllers: HashMap> = HashMap::new(); +pub fn get_controllers() -> (String, HashMap) { + let mut controllers: HashMap = HashMap::new(); let mut default = None; @@ -198,7 +189,7 @@ pub fn get_controllers() -> (String, HashMap>) { default = Some(key.clone()); } - controllers.insert(key, Box::new(name)); + controllers.insert(key, name); } } } diff --git a/src/main.rs b/src/main.rs index 41254d3..0a5613b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,22 +23,22 @@ fn main() { Some("raw") => Box::new(RawController::new(p)), Some("lin") => Box::new(LinController::new(p)), Some("log") => Box::new(LogController::new(p)), - Some(_) | None => panic!("{}", ERROR_MSG), + Some(_) | None => panic!("{ERROR_MSG}"), }; if matches.is_present("list") { for ctrl in ctrls.keys() { - println!("{}", ctrl); + println!("{ctrl}"); } exit(exitcode::OK); } else if let Some(value) = matches.value_of("set") { - let new_value = value.parse::().unwrap(); + let new_value = value.parse::().unwrap(); controller.set_brightness(new_value); } else if let Some(value) = matches.value_of("inc") { - let new_value = controller.get_brightness() + value.parse::().unwrap(); + let new_value = controller.get_brightness() + value.parse::().unwrap(); controller.set_brightness(new_value.min(controller.get_max_brightness())); } else if let Some(value) = matches.value_of("dec") { - let new_value = controller.get_brightness() - value.parse::().unwrap(); + let new_value = controller.get_brightness() - value.parse::().unwrap(); controller.set_brightness(new_value.max(0)); } else if matches.is_present("get") { println!("{}", controller.get_brightness()); From 0eb1534e42f13c5966e7406a78d2f7aa0b292499 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 01:17:20 +0200 Subject: [PATCH 136/320] use long names --- src/cli.rs | 6 +++--- src/main.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index d5c97b8..ca49ce5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -42,20 +42,20 @@ pub fn build_cli() -> App<'static> { .help("Prints current brightness value"), ) .arg( - Arg::with_name("zer") + Arg::with_name("zero") .short('z') .long("zero") .help("Sets brightness to lowest value"), ) .arg( - Arg::with_name("ful") + Arg::with_name("full") .short('f') .long("full") .help("Sets brightness to highest value"), ) .group( ArgGroup::with_name("brightness_control") - .args(&["set", "inc", "dec", "get", "zer", "ful"]), + .args(&["set", "inc", "dec", "get", "zero", "full"]), ) .arg( Arg::with_name("list") diff --git a/src/main.rs b/src/main.rs index 0a5613b..2653096 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,9 +42,9 @@ fn main() { controller.set_brightness(new_value.max(0)); } else if matches.is_present("get") { println!("{}", controller.get_brightness()); - } else if matches.is_present("zer") { + } else if matches.is_present("zero") { controller.set_brightness(0); - } else if matches.is_present("ful") { + } else if matches.is_present("full") { controller.set_brightness(controller.get_max_brightness()); } else { build_cli().print_long_help().unwrap(); From 626024a115fded2dc79b5a3d0b9fbb55882f1187 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 01:23:26 +0200 Subject: [PATCH 137/320] add woodpecker config --- .woodpecker/lamp.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .woodpecker/lamp.yml diff --git a/.woodpecker/lamp.yml b/.woodpecker/lamp.yml new file mode 100644 index 0000000..4463cc9 --- /dev/null +++ b/.woodpecker/lamp.yml @@ -0,0 +1,29 @@ +branches: ["main"] + +matrix: + RELEASE_DEBUG: + - "" + - "--release" + +pipeline: + lint: + image: rust:alpine + pull: true + when: + path: + include: ["src/**", "Cargo.toml", "Cargo.lock", ".woodpecker/lamp.yml"] + commands: + - apk add musl-dev + - rustup component add clippy rustfmt + - cargo fmt -- --check + - cargo clippy --locked ${RELEASE_DEBUG} -- --deny clippy::all --deny warnings + + build: + image: rust:alpine + pull: true + when: + path: + include: ["src/**", "Cargo.toml", "Cargo.lock", ".woodpecker/lamp.yml"] + commands: + - apk add musl-dev + - cargo build --locked ${RELEASE_DEBUG} From 96f92e0d3fd6fc3a9119922cc80704b991969b27 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 01:25:26 +0200 Subject: [PATCH 138/320] trigger ci From fcc49740c740a821900764699d76b973e60ba6a3 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 02:22:57 +0200 Subject: [PATCH 139/320] show default controller in output --- src/cli.rs | 8 ++------ src/main.rs | 5 ++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index ca49ce5..893a3c5 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,7 +3,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use clap::{App, Arg, ArgGroup}; +use clap::{App, Arg}; pub fn build_cli() -> App<'static> { App::new("lamp") @@ -53,16 +53,12 @@ pub fn build_cli() -> App<'static> { .long("full") .help("Sets brightness to highest value"), ) - .group( - ArgGroup::with_name("brightness_control") - .args(&["set", "inc", "dec", "get", "zero", "full"]), - ) .arg( Arg::with_name("list") .short('l') .long("list") .help("Lists all available brightness and led controllers") - .conflicts_with_all(&["brightness_control"]), + .exclusive(true) ) .arg( Arg::with_name("ctrl_type") diff --git a/src/main.rs b/src/main.rs index 2653096..aa37e9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,8 +27,11 @@ fn main() { }; if matches.is_present("list") { + println!("{default_ctrl} [default]"); for ctrl in ctrls.keys() { - println!("{ctrl}"); + if ctrl != &default_ctrl { + println!("{ctrl}"); + } } exit(exitcode::OK); } else if let Some(value) = matches.value_of("set") { From 8f16e5968044ec3f010c1dd62d2b7d0fe7c34d39 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 02:25:31 +0200 Subject: [PATCH 140/320] format --- src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index 893a3c5..6531916 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -58,7 +58,7 @@ pub fn build_cli() -> App<'static> { .short('l') .long("list") .help("Lists all available brightness and led controllers") - .exclusive(true) + .exclusive(true), ) .arg( Arg::with_name("ctrl_type") From ba2844c7d1386e7dd2ccbfc911c2e50b34153b1e Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 02:34:51 +0200 Subject: [PATCH 141/320] prepare crates.io release --- Cargo.lock | 18 +++++++++--------- Cargo.toml | 8 +++++++- build.rs | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0141983..52a3944 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,15 +89,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "lamp" -version = "0.2.1" -dependencies = [ - "clap", - "clap_complete", - "exitcode", -] - [[package]] name = "libc" version = "0.2.126" @@ -110,6 +101,15 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" +[[package]] +name = "r_lamp" +version = "0.2.1" +dependencies = [ + "clap", + "clap_complete", + "exitcode", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 16a773b..16053ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,15 @@ # along with this program. If not, see . [package] -name = "lamp" +name = "r_lamp" version = "0.2.1" authors = ["crapStone "] +license = "GPL-3.0-or-later" +description = "A Linux backlight utility inspired by acpibacklight" +homepage = "https://codeberg.org/crapStone/lamp" +repository = "https://codeberg.org/crapStone/lamp" +readme = "README.md" +categories = ["command-line-utilities"] edition = "2021" build = "build.rs" diff --git a/build.rs b/build.rs index 8318948..e7c82bf 100644 --- a/build.rs +++ b/build.rs @@ -13,7 +13,7 @@ use clap_complete::{generate_to, shells, Generator}; include!("src/cli.rs"); -const OUTDIR: &str = "completions"; +const OUTDIR: &str = "target/completions"; fn main() -> Result<(), Error> { let mut cmd = build_cli(); From 15723388d215dd6caf56335f7f6f52e0f58e4e39 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 03:42:17 +0200 Subject: [PATCH 142/320] add aur package to readme, closes #3 --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8f660aa..a56140f 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,9 @@ Lamp is a backlight control program written in Rust and inspired by [acpibacklight](https://gitlab.com/wavexx/acpilight). + +## Install + +Binary packages for the following systems are currently available. + +[![Packaging status](https://repology.org/badge/vertical-allrepos/lamp.svg)](https://repology.org/project/lamp/versions) From 80081c5e60efdb3645b9bf3bac4dc6ef5d90c095 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 04:40:10 +0200 Subject: [PATCH 143/320] i got the name on crates.io!!! --- Cargo.lock | 18 +++++++++--------- Cargo.toml | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52a3944..ce84416 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "lamp" +version = "0.2.2" +dependencies = [ + "clap", + "clap_complete", + "exitcode", +] + [[package]] name = "libc" version = "0.2.126" @@ -101,15 +110,6 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" -[[package]] -name = "r_lamp" -version = "0.2.1" -dependencies = [ - "clap", - "clap_complete", - "exitcode", -] - [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 16053ed..0cda71c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ # along with this program. If not, see . [package] -name = "r_lamp" -version = "0.2.1" +name = "lamp" +version = "0.2.2" authors = ["crapStone "] license = "GPL-3.0-or-later" description = "A Linux backlight utility inspired by acpibacklight" From 6f84078759887dcc8f2662032eed1578206e0bb9 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 14:10:40 +0200 Subject: [PATCH 144/320] improve readme, closes #2 --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index a56140f..6764663 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,46 @@ Lamp is a backlight control program written in Rust and inspired by [acpibacklight](https://gitlab.com/wavexx/acpilight). +## Features + +In contrast to acpilight lamp is not backwards compatible with xbacklight. +It is intended to be used as a standalone replacement for new scripts. + +```none + -d, --decrease Decreases brightness + -f, --full Sets brightness to highest value + -g, --get Prints current brightness value + -h, --help Print help information + -i, --increase Increases brightness + -l, --list Lists all available brightness and led controllers + -s, --set Sets brightness to given value + -t, --type choose controller type [default: lin] [possible values: raw, + lin, log] + -V, --version Print version information + -z, --zero Sets brightness to lowest value +``` + ## Install Binary packages for the following systems are currently available. [![Packaging status](https://repology.org/badge/vertical-allrepos/lamp.svg)](https://repology.org/project/lamp/versions) + +You can also install it via `cargo`: + +```bash +cargo install lamp +``` + +You have to make sure, that you have write access to `/sys/class/backlight/`. +This can be achieved by using udev rules like `90-backlight.rules` in this repo. + +## Build + +lamp is a pure Rust project so you can simply run `cargo build` after installing the Rust toolchain. + +Formatting is done via `cargo fmt` with the default rules and in the pipeline `clippy` is run with the following arguments: + +```bash +cargo clippy -- --deny clippy::all --deny warnings +``` From 620ba2bbe8119b180b5672855b0a9bf31a806d8f Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 14:37:12 +0200 Subject: [PATCH 145/320] Add cli option to select device to control (#6) Co-authored-by: crapStone Reviewed-on: https://codeberg.org/crapStone/lamp/pulls/6 --- README.md | 3 ++- src/cli.rs | 22 +++++++++++++++------- src/main.rs | 15 ++++++++++++++- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6764663..bd40c09 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,13 @@ In contrast to acpilight lamp is not backwards compatible with xbacklight. It is intended to be used as a standalone replacement for new scripts. ```none + -c, --controller Select device to control -d, --decrease Decreases brightness -f, --full Sets brightness to highest value -g, --get Prints current brightness value -h, --help Print help information -i, --increase Increases brightness - -l, --list Lists all available brightness and led controllers + -l, --list Lists all devices with controllable brightness and led values -s, --set Sets brightness to given value -t, --type choose controller type [default: lin] [possible values: raw, lin, log] diff --git a/src/cli.rs b/src/cli.rs index 6531916..1d250b8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -15,25 +15,25 @@ pub fn build_cli() -> App<'static> { Arg::with_name("set") .short('s') .long("set") - .value_name("VALUE") .help("Sets brightness to given value") - .takes_value(true), + .takes_value(true) + .value_name("VALUE"), ) .arg( Arg::with_name("inc") .short('i') .long("increase") - .value_name("PERCENT") .help("Increases brightness") - .takes_value(true), + .takes_value(true) + .value_name("PERCENT"), ) .arg( Arg::with_name("dec") .short('d') .long("decrease") - .value_name("PERCENT") .help("Decreases brightness") - .takes_value(true), + .takes_value(true) + .value_name("PERCENT"), ) .arg( Arg::with_name("get") @@ -57,9 +57,17 @@ pub fn build_cli() -> App<'static> { Arg::with_name("list") .short('l') .long("list") - .help("Lists all available brightness and led controllers") + .help("Lists all devices with controllable brightness and led values") .exclusive(true), ) + .arg( + Arg::with_name("controller") + .short('c') + .long("controller") + .help("Select device to control") + .value_name("DEVICE") + .takes_value(true), + ) .arg( Arg::with_name("ctrl_type") .short('t') diff --git a/src/main.rs b/src/main.rs index aa37e9f..45a2d0a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,20 @@ fn main() { let (default_ctrl, ctrls) = controllers::get_controllers(); - let p = ctrls.get(&default_ctrl).unwrap().to_owned(); + let p = match matches.value_of("controller") { + Some(ctrl) => { + let p = ctrls.get(ctrl); + if p == None { + eprintln!("no device with name '{ctrl}' found"); + eprintln!("use --list to ge a list of all available devices"); + exit(exitcode::DATAERR); + } + + p.unwrap().to_owned() + } + None => ctrls.get(&default_ctrl).unwrap().to_owned(), + }; + let controller: Box = match matches.value_of("ctrl_type") { Some("raw") => Box::new(RawController::new(p)), Some("lin") => Box::new(LinController::new(p)), From 61abc5cb7c250aea50047c05eefbc847f688da86 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 14:39:14 +0200 Subject: [PATCH 146/320] prepare 0.3.0 release --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce84416..792b304 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ dependencies = [ [[package]] name = "lamp" -version = "0.2.2" +version = "0.3.0" dependencies = [ "clap", "clap_complete", diff --git a/Cargo.toml b/Cargo.toml index 0cda71c..10719ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "lamp" -version = "0.2.2" +version = "0.3.0" authors = ["crapStone "] license = "GPL-3.0-or-later" description = "A Linux backlight utility inspired by acpibacklight" From 4624a98039ded0b83dd42849627c9f7d6ff2a2dc Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 16:15:12 +0200 Subject: [PATCH 147/320] add badges to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bd40c09..0822cef 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Lamp +[![status-badge](https://ci.codeberg.org/api/badges/crapStone/lamp/status.svg)](https://ci.codeberg.org/crapStone/lamp) +[![Crates.io](https://img.shields.io/crates/v/lamp.svg)](https://crates.io/crates/lamp) + Lamp is a backlight control program written in Rust and inspired by [acpibacklight](https://gitlab.com/wavexx/acpilight). From 150b6ff30bcf47fb61fdde515b309002fcd303cf Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 16 Jul 2022 16:40:53 +0200 Subject: [PATCH 148/320] removed unnecessary unwraps, closes #4 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cli.rs | 2 +- src/controllers.rs | 16 +++++++++++----- src/main.rs | 21 +++++++++++++++------ 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 792b304..96541aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ dependencies = [ [[package]] name = "lamp" -version = "0.3.0" +version = "0.3.1" dependencies = [ "clap", "clap_complete", diff --git a/Cargo.toml b/Cargo.toml index 10719ff..396c25a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ [package] name = "lamp" -version = "0.3.0" +version = "0.3.1" authors = ["crapStone "] license = "GPL-3.0-or-later" description = "A Linux backlight utility inspired by acpibacklight" diff --git a/src/cli.rs b/src/cli.rs index 1d250b8..039bbbf 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -64,7 +64,7 @@ pub fn build_cli() -> App<'static> { Arg::with_name("controller") .short('c') .long("controller") - .help("Select device to control") + .help("Select device to control, defaults to the first device found") .value_name("DEVICE") .takes_value(true), ) diff --git a/src/controllers.rs b/src/controllers.rs index cc49b85..d4c447d 100644 --- a/src/controllers.rs +++ b/src/controllers.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::fs::{File, OpenOptions}; -use std::io::prelude::*; +use std::io::{self, prelude::*}; use std::path::{Path, PathBuf}; use std::process::exit; @@ -174,15 +174,15 @@ fn read_file_to_int(path: PathBuf) -> u32 { /// Searches through all paths in `SYS_PATHS` and creates a `HashMap` with the name and absolute path. /// /// It returns a `Tuple` of the default backlight name and the `HashMap`. -pub fn get_controllers() -> (String, HashMap) { +pub fn get_controllers() -> Result<(String, HashMap), io::Error> { let mut controllers: HashMap = HashMap::new(); let mut default = None; for path in SYS_PATHS { if Path::new(path).exists() { - for name in Path::new(path).read_dir().unwrap() { - let name = name.unwrap().path(); + for name in Path::new(path).read_dir()? { + let name = name?.path(); let key = String::from(name.file_name().unwrap().to_str().unwrap()); if default.is_none() { @@ -194,5 +194,11 @@ pub fn get_controllers() -> (String, HashMap) { } } - (default.unwrap(), controllers) + Ok(( + default.unwrap_or_else(|| { + eprintln!("no devices found"); + exit(exitcode::OSFILE) + }), + controllers, + )) } diff --git a/src/main.rs b/src/main.rs index 45a2d0a..52e3180 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,10 @@ fn main() { let app = build_cli(); let matches = app.get_matches(); - let (default_ctrl, ctrls) = controllers::get_controllers(); + let (default_ctrl, ctrls) = controllers::get_controllers().unwrap_or_else(|why| { + eprintln!("an error occured when reading devices: '{why}'"); + exit(exitcode::IOERR) + }); let p = match matches.value_of("controller") { Some(ctrl) => { @@ -48,14 +51,14 @@ fn main() { } exit(exitcode::OK); } else if let Some(value) = matches.value_of("set") { - let new_value = value.parse::().unwrap(); + let new_value = str_to_int(value); controller.set_brightness(new_value); } else if let Some(value) = matches.value_of("inc") { - let new_value = controller.get_brightness() + value.parse::().unwrap(); + let new_value = controller.get_brightness() + str_to_int(value); controller.set_brightness(new_value.min(controller.get_max_brightness())); } else if let Some(value) = matches.value_of("dec") { - let new_value = controller.get_brightness() - value.parse::().unwrap(); - controller.set_brightness(new_value.max(0)); + let new_value = controller.get_brightness() - str_to_int(value); + controller.set_brightness(new_value); } else if matches.is_present("get") { println!("{}", controller.get_brightness()); } else if matches.is_present("zero") { @@ -65,8 +68,14 @@ fn main() { } else { build_cli().print_long_help().unwrap(); } +} - exit(exitcode::OK); +#[inline(always)] +fn str_to_int(value: &str) -> u32 { + value.parse().unwrap_or_else(|_| { + eprintln!("cannot parse '{value}' as positive integer"); + exit(exitcode::DATAERR); + }) } // https://xkcd.com/2200/ From e06900d5e5a38c01efea25ca72340d1c0be45321 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 8 Aug 2022 15:25:31 +0200 Subject: [PATCH 149/320] fix lint issue --- server/certificates/certificates.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 2684dfa..8761c93 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -12,7 +12,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "os" "strconv" "strings" @@ -329,7 +328,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce var myAcmeAccount AcmeAccount var myAcmeConfig *lego.Config - if account, err := ioutil.ReadFile(configFile); err == nil { + if account, err := os.ReadFile(configFile); err == nil { if err := json.Unmarshal(account, &myAcmeAccount); err != nil { return nil, err } @@ -395,9 +394,9 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) select {} } - err = ioutil.WriteFile(configFile, acmeAccountJSON, 0o600) + err = os.WriteFile(configFile, acmeAccountJSON, 0o600) if err != nil { - log.Printf("[FAIL] Error during ioutil.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) + log.Printf("[FAIL] Error during os.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) select {} } } From 876a53d9a245c11c58b61617052f7f2281d16358 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 12 Aug 2022 05:06:26 +0200 Subject: [PATCH 150/320] Improve logging (#116) - Actually log useful information at their respective log level. - Add logs in hot-paths to be able to deep-dive and debug specific requests (see server/handler.go) - Add more information to existing fields(e.g. the host that the user is visiting, this was noted by @fnetX). Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/116 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- cmd/flags.go | 12 +++---- cmd/main.go | 15 ++++---- go.mod | 9 ++--- go.sum | 16 +++++---- integration/get_test.go | 10 +++--- integration/main_test.go | 10 +++--- server/certificates/certificates.go | 54 ++++++++++++++--------------- server/database/setup.go | 2 +- server/handler.go | 40 ++++++++++++--------- server/setup.go | 4 +-- server/try.go | 1 + server/upstream/upstream.go | 23 ++++++------ 12 files changed, 107 insertions(+), 89 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 78040be..3c1ac99 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -5,12 +5,6 @@ import ( ) var ServeFlags = []cli.Flag{ - &cli.BoolFlag{ - Name: "verbose", - // TODO: Usage - EnvVars: []string{"DEBUG"}, - }, - // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static // pages, or used for comparison in CNAME lookups. Static pages can be accessed through // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". @@ -69,6 +63,12 @@ var ServeFlags = []cli.Flag{ // TODO: desc EnvVars: []string{"ENABLE_HTTP_SERVER"}, }, + &cli.StringFlag{ + Name: "log-level", + Value: "warn", + Usage: "specify at which log level should be logged. Possible options: info, warn, error, fatal", + EnvVars: []string{"LOG_LEVEL"}, + }, // ACME &cli.StringFlag{ diff --git a/cmd/main.go b/cmd/main.go index 80fb666..f57eb60 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net" + "os" "strings" "time" @@ -36,10 +37,12 @@ var BlacklistedPaths = [][]byte{ // Serve sets up and starts the web server. func Serve(ctx *cli.Context) error { - verbose := ctx.Bool("verbose") - if !verbose { - zerolog.SetGlobalLevel(zerolog.InfoLevel) + // Initalize the logger. + logLevel, err := zerolog.ParseLevel(ctx.String("log-level")) + if err != nil { + return err } + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") giteaAPIToken := ctx.String("gitea-api-token") @@ -101,7 +104,7 @@ func Serve(ctx *cli.Context) error { log.Info().Msgf("Listening on https://%s", listeningAddress) listener, err := net.Listen("tcp", listeningAddress) if err != nil { - return fmt.Errorf("couldn't create listener: %s", err) + return fmt.Errorf("couldn't create listener: %v", err) } // TODO: make "key-database.pogreb" set via flag @@ -134,7 +137,7 @@ func Serve(ctx *cli.Context) error { if enableHTTPServer { go func() { - log.Info().Timestamp().Msg("Start listening on :80") + log.Info().Msg("Start HTTP server listening on :80") err := httpServer.ListenAndServe("[::]:80") if err != nil { log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") @@ -143,7 +146,7 @@ func Serve(ctx *cli.Context) error { } // Start the web fastServer - log.Info().Timestamp().Msgf("Start listening on %s", listener.Addr()) + log.Info().Msgf("Start listening on %s", listener.Addr()) err = fastServer.Serve(listener) if err != nil { log.Panic().Err(err).Msg("Couldn't start fastServer") diff --git a/go.mod b/go.mod index 13df431..64288de 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ require ( github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 + github.com/joho/godotenv v1.4.0 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad - github.com/rs/zerolog v1.26.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 github.com/valyala/fasthttp v1.31.0 @@ -60,7 +60,6 @@ require ( github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/jarcoal/httpmock v1.0.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/joho/godotenv v1.4.0 // indirect github.com/json-iterator/go v1.1.7 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/klauspost/compress v1.13.4 // indirect @@ -72,7 +71,8 @@ require ( github.com/liquidweb/go-lwApi v0.0.5 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect github.com/liquidweb/liquidweb-go v1.6.3 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/miekg/dns v1.1.43 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect @@ -92,6 +92,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.3.0 // indirect + github.com/rs/zerolog v1.27.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/sacloud/libsacloud v1.36.2 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect @@ -111,7 +112,7 @@ require ( golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect + golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect diff --git a/go.sum b/go.sum index f009f5e..4ac6eb6 100644 --- a/go.sum +++ b/go.sum @@ -95,7 +95,7 @@ github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= @@ -321,12 +321,16 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -428,8 +432,8 @@ github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= -github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= +github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= +github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -510,7 +514,6 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -569,7 +572,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -668,8 +670,11 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -723,7 +728,6 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/integration/get_test.go b/integration/get_test.go index 8af40f0..191fa3f 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -7,16 +7,16 @@ import ( "bytes" "crypto/tls" "io" + "log" "net/http" "net/http/cookiejar" "testing" - "github.com/rs/zerolog/log" "github.com/stretchr/testify/assert" ) func TestGetRedirect(t *testing.T) { - log.Printf("=== TestGetRedirect ===\n") + log.Println("=== TestGetRedirect ===") // test custom domain redirect resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430") assert.NoError(t, err) @@ -28,7 +28,7 @@ func TestGetRedirect(t *testing.T) { } func TestGetContent(t *testing.T) { - log.Printf("=== TestGetContent ===\n") + log.Println("=== TestGetContent ===") // test get image resp, err := getTestHTTPSClient().Get("https://magiclike.localhost.mock.directory:4430/images/827679288a.jpg") assert.NoError(t, err) @@ -64,7 +64,7 @@ func TestGetContent(t *testing.T) { } func TestCustomDomain(t *testing.T) { - log.Printf("=== TestCustomDomain ===\n") + log.Println("=== TestCustomDomain ===") resp, err := getTestHTTPSClient().Get("https://mock-pages.codeberg-test.org:4430/README.md") assert.NoError(t, err) if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { @@ -76,7 +76,7 @@ func TestCustomDomain(t *testing.T) { } func TestGetNotFound(t *testing.T) { - log.Printf("=== TestGetNotFound ===\n") + log.Println("=== TestGetNotFound ===") // test custom not found pages resp, err := getTestHTTPSClient().Get("https://crystal.localhost.mock.directory:4430/pages-404-demo/blah") assert.NoError(t, err) diff --git a/integration/main_test.go b/integration/main_test.go index c8f524f..06d553f 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -5,25 +5,25 @@ package integration import ( "context" + "log" "os" "testing" "time" "codeberg.org/codeberg/pages/cmd" - "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" ) func TestMain(m *testing.M) { - log.Printf("=== TestMain: START Server ===\n") + log.Println("=== TestMain: START Server ===") serverCtx, serverCancel := context.WithCancel(context.Background()) if err := startServer(serverCtx); err != nil { - log.Fatal().Msgf("could not start server: %v", err) + log.Fatalf("could not start server: %v", err) } defer func() { serverCancel() - log.Printf("=== TestMain: Server STOPED ===\n") + log.Println("=== TestMain: Server STOPED ===") }() time.Sleep(10 * time.Second) @@ -48,7 +48,7 @@ func startServer(ctx context.Context) error { go func() { if err := app.RunContext(ctx, args); err != nil { - log.Fatal().Msgf("run server error: %v", err) + log.Fatalf("run server error: %v", err) } }() diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 8761c93..8944468 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -228,7 +228,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs res.CSR = nil // acme client doesn't like CSR to be set tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { - log.Printf("Couldn't renew certificate for %s: %s", sni, err) + log.Error().Msgf("Couldn't renew certificate for %s: %v", string(sni), err) } })() } @@ -271,10 +271,10 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if acmeUseRateLimits { acmeClientRequestLimit.Take() } - log.Printf("Renewing certificate for %v", domains) + log.Debug().Msgf("Renewing certificate for: %v", domains) res, err = acmeClient.Certificate.Renew(*renew, true, false, "") if err != nil { - log.Printf("Couldn't renew certificate for %v, trying to request a new one: %s", domains, err) + log.Error().Err(err).Msgf("Couldn't renew certificate for %v, trying to request a new one", domains) res = nil } } @@ -289,7 +289,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re acmeClientOrderLimit.Take() acmeClientRequestLimit.Take() } - log.Printf("Requesting new certificate for %v", domains) + log.Debug().Msgf("Re-requesting new certificate for %v", domains) res, err = acmeClient.Certificate.Obtain(certificate.ObtainRequest{ Domains: domains, Bundle: true, @@ -297,7 +297,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re }) } if err != nil { - log.Printf("Couldn't obtain certificate for %v: %s", domains, err) + log.Error().Err(err).Msgf("Couldn't obtain again a certificate or %v", domains) if renew != nil && renew.CertURL != "" { tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { @@ -311,7 +311,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re } return mockCert(domains[0], err.Error(), string(mainDomainSuffix), keyDatabase), err } - log.Printf("Obtained certificate for %v", domains) + log.Debug().Msgf("Obtained certificate for %v", domains) if err := keyDatabase.Put(name, res); err != nil { return tls.Certificate{}, err @@ -344,7 +344,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce _, err := lego.NewClient(myAcmeConfig) if err != nil { // TODO: should we fail hard instead? - log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } return myAcmeConfig, nil } else if !os.IsNotExist(err) { @@ -365,13 +365,13 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 tempClient, err := lego.NewClient(myAcmeConfig) if err != nil { - log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { // accept terms & log in to EAB if acmeEabKID == "" || acmeEabHmac == "" { reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) if err != nil { - log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") } else { myAcmeAccount.Registration = reg } @@ -382,7 +382,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce HmacEncoded: acmeEabHmac, }) if err != nil { - log.Printf("[ERROR] Can't register ACME account, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") } else { myAcmeAccount.Registration = reg } @@ -391,12 +391,12 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce if myAcmeAccount.Registration != nil { acmeAccountJSON, err := json.Marshal(myAcmeAccount) if err != nil { - log.Printf("[FAIL] Error during json.Marshal(myAcmeAccount), waiting for manual restart to avoid rate limits: %s", err) + log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") select {} } err = os.WriteFile(configFile, acmeAccountJSON, 0o600) if err != nil { - log.Printf("[FAIL] Error during os.WriteFile(\"acme-account.json\"), waiting for manual restart to avoid rate limits: %s", err) + log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") select {} } } @@ -414,38 +414,38 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * acmeClient, err = lego.NewClient(acmeConfig) if err != nil { - log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { - log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) + log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") } if enableHTTPServer { err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) if err != nil { - log.Printf("[ERROR] Can't create HTTP-01 provider: %s", err) + log.Error().Err(err).Msg("Can't create HTTP-01 provider") } } } mainDomainAcmeClient, err = lego.NewClient(acmeConfig) if err != nil { - log.Printf("[ERROR] Can't create ACME client, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { if dnsProvider == "" { // using mock server, don't use wildcard certs err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { - log.Printf("[ERROR] Can't create TLS-ALPN-01 provider: %s", err) + log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") } } else { provider, err := dns.NewDNSChallengeProviderByName(dnsProvider) if err != nil { - log.Printf("[ERROR] Can't create DNS Challenge provider: %s", err) + log.Error().Err(err).Msg("Can't create DNS Challenge provider") } err = mainDomainAcmeClient.Challenge.SetDNS01Provider(provider) if err != nil { - log.Printf("[ERROR] Can't create DNS-01 provider: %s", err) + log.Error().Err(err).Msg("Can't create DNS-01 provider") } } } @@ -453,7 +453,7 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * if mainCertBytes == nil { _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { - log.Printf("[ERROR] Couldn't renew main domain certificate, continuing with mock certs only: %s", err) + log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only") } } @@ -481,7 +481,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi if err != nil || !tlsCertificates[0].NotAfter.After(now) { err := certDB.Delete(string(key)) if err != nil { - log.Printf("[ERROR] Deleting expired certificate for %s failed: %s", string(key), err) + log.Error().Err(err).Msgf("Deleting expired certificate for %q failed", string(key)) } else { expiredCertCount++ } @@ -489,22 +489,22 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } key, resBytes, err = keyDatabaseIterator.Next() } - log.Printf("[INFO] Removed %d expired certificates from the database", expiredCertCount) + log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount) // compact the database msg, err := certDB.Compact() if err != nil { - log.Printf("[ERROR] Compacting key database failed: %s", err) + log.Error().Err(err).Msg("Compacting key database failed") } else { - log.Printf("[INFO] Compacted key database (%s)", msg) + log.Debug().Msgf("Compacted key database: %s", msg) } // update main cert res, err := certDB.Get(string(mainDomainSuffix)) if err != nil { - log.Err(err).Msgf("could not get cert for domain '%s'", mainDomainSuffix) + log.Error().Msgf("Couldn't get cert for domain %q", mainDomainSuffix) } else if res == nil { - log.Error().Msgf("Couldn't renew certificate for main domain: %s", "expected main domain cert to exist, but it's missing - seems like the database is corrupted") + log.Error().Msgf("Couldn't renew certificate for main domain %q expected main domain cert to exist, but it's missing - seems like the database is corrupted", string(mainDomainSuffix)) } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) @@ -513,7 +513,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi go (func() { _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { - log.Printf("[ERROR] Couldn't renew certificate for main domain: %s", err) + log.Error().Err(err).Msg("Couldn't renew certificate for main domain") } })() } diff --git a/server/database/setup.go b/server/database/setup.go index bbcf431..1c5a0af 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -72,7 +72,7 @@ func (p aDB) sync() { for { err := p.intern.Sync() if err != nil { - log.Err(err).Msg("Syncing cert database failed") + log.Error().Err(err).Msg("Syncing cert database failed") } select { case <-p.ctx.Done(): diff --git a/server/handler.go b/server/handler.go index 11da0a5..50fbcc5 100644 --- a/server/handler.go +++ b/server/handler.go @@ -85,7 +85,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // also disallow search indexing and add a Link header to the canonical URL. tryBranch := func(log zerolog.Logger, repo, branch string, path []string, canonicalLink string) bool { if repo == "" { - log.Debug().Msg("tryBranch: repo == ''") + log.Debug().Msg("tryBranch: repo is empty") return false } @@ -120,10 +120,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, return true } - log.Debug().Msg("preparations") + log.Debug().Msg("Preparing") if rawDomain != nil && bytes.Equal(trimmedHost, rawDomain) { // Serve raw content from RawDomain - log.Debug().Msg("raw domain") + log.Debug().Msg("Serving raw domain") targetOptions.TryIndexPages = false if targetOptions.ForbiddenMimeTypes == nil { @@ -143,28 +143,28 @@ func Handler(mainDomainSuffix, rawDomain []byte, // raw.codeberg.org/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { - log.Debug().Msg("raw domain preparations, now trying with specified branch") + log.Debug().Msg("Preparing raw domain, now trying with specified branch") if tryBranch(log, targetRepo, pathElements[2][1:], pathElements[3:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { - log.Debug().Msg("tryBranch, now trying upstream 1") + log.Info().Msg("tryBranch, now trying upstream 1") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } - log.Debug().Msg("missing branch") + log.Warn().Msg("Path missed a branch") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } - log.Debug().Msg("raw domain preparations, now trying with default branch") + log.Debug().Msg("Preparing raw domain, now trying with default branch") tryBranch(log, targetRepo, "", pathElements[2:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) - log.Debug().Msg("tryBranch, now trying upstream 2") + log.Info().Msg("tryBranch, now trying upstream 2") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) @@ -172,7 +172,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } else if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix - log.Debug().Msg("main domain suffix") + log.Info().Msg("Serve pages from main domain suffix") pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") targetOwner = string(bytes.TrimSuffix(trimmedHost, mainDomainSuffix)) @@ -194,16 +194,17 @@ func Handler(mainDomainSuffix, rawDomain []byte, return } - log.Debug().Msg("main domain preparations, now trying with specified repo & branch") + log.Debug().Msg("Preparing main domain, now trying with specified repo & branch") if tryBranch(log, pathElements[0], pathElements[1][1:], pathElements[2:], "/"+pathElements[0]+"/%p", ) { - log.Debug().Msg("tryBranch, now trying upstream 3") + log.Info().Msg("tryBranch, now trying upstream 3") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { + log.Warn().Msg("tryBranch: upstream 3 failed") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } return @@ -212,14 +213,15 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Check if the first directory is a branch for the "pages" repo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { - log.Debug().Msg("main domain preparations, now trying with specified branch") + log.Debug().Msg("Preparing main domain, now trying with specified branch") if tryBranch(log, "pages", pathElements[0][1:], pathElements[1:], "/%p") { - log.Debug().Msg("tryBranch, now trying upstream 4") + log.Info().Msg("tryBranch, now trying upstream 4") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) } else { + log.Warn().Msg("tryBranch: upstream 4 failed") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) } return @@ -231,7 +233,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(log, pathElements[0], "pages", pathElements[1:], "") { - log.Debug().Msg("tryBranch, now trying upstream 5") + log.Info().Msg("tryBranch, now trying upstream 5") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) @@ -243,7 +245,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with default repo/branch") if tryBranch(log, "pages", "", pathElements, "") { - log.Debug().Msg("tryBranch, now trying upstream 6") + log.Info().Msg("tryBranch, now trying upstream 6") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) @@ -251,6 +253,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Couldn't find a valid repo/branch + html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } else { @@ -272,11 +275,12 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Try to use the given repo on the given branch or the default branch - log.Debug().Msg("custom domain preparations, now trying with details from DNS") + log.Debug().Msg("Preparing custom domain, now trying with details from DNS") if tryBranch(log, targetRepo, targetBranch, pathElements, canonicalLink) { canonicalDomain, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), canonicalDomainCache) if !valid { + log.Warn().Msg("Custom domains, domain from DNS isn't valid/canonical") html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { @@ -287,17 +291,19 @@ func Handler(mainDomainSuffix, rawDomain []byte, return } + log.Warn().Msg("Custom domains, targetOwner from DNS is empty") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } - log.Debug().Msg("tryBranch, now trying upstream 7") + log.Info().Msg("tryBranch, now trying upstream 7 %s") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) return } + log.Warn().Msg("Couldn't handle request, none of the options succeed") html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) return } diff --git a/server/setup.go b/server/setup.go index ea96d1e..176bb42 100644 --- a/server/setup.go +++ b/server/setup.go @@ -2,11 +2,11 @@ package server import ( "bytes" + "fmt" "net/http" "time" "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/server/cache" @@ -16,7 +16,7 @@ import ( type fasthttpLogger struct{} func (fasthttpLogger) Printf(format string, args ...interface{}) { - log.Printf("[FASTHTTP] "+format, args...) + log.Printf("FastHTTP: %s", fmt.Sprintf(format, args...)) } func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { diff --git a/server/try.go b/server/try.go index 254d3ec..24831c4 100644 --- a/server/try.go +++ b/server/try.go @@ -41,6 +41,7 @@ func tryUpstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, targetOptions.TargetRepo = targetRepo targetOptions.TargetBranch = targetBranch targetOptions.TargetPath = targetPath + targetOptions.Host = string(trimmedHost) // Try to request the file from the Gitea API if !targetOptions.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 9b7464e..4371e88 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -3,7 +3,6 @@ package upstream import ( "bytes" "errors" - "fmt" "io" "strings" "time" @@ -33,7 +32,10 @@ type Options struct { TargetBranch, TargetPath, - DefaultMimeType string + // Used for debugging purposes. + Host string + + DefaultMimeType string ForbiddenMimeTypes map[string]bool TryIndexPages bool BranchTimestamp time.Time @@ -44,7 +46,7 @@ type Options struct { // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { - log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() + log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath, o.Host}).Logger() // Check if the branch exists and when it was modified if o.BranchTimestamp.IsZero() { @@ -70,7 +72,8 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, return true } } - log.Debug().Msg("preparations") + + log.Debug().Msg("Preparing") // Make a GET request to the upstream URL uri := o.generateUri() @@ -82,7 +85,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } else { res, err = giteaClient.ServeRawContent(uri) } - log.Debug().Msg("acquisition") + log.Debug().Msg("Aquisting") // Handle errors if (err != nil && errors.Is(err, gitea.ErrorNotFound)) || (res == nil && !cachedResponse.Exists) { @@ -136,7 +139,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, return false } if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { - fmt.Printf("Couldn't fetch contents from \"%s\": %s (status code %d)\n", uri, err, res.StatusCode()) + log.Warn().Msgf("Couldn't fetch contents from %q: %v (status code %d)", uri, err, res.StatusCode()) html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } @@ -155,7 +158,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, ctx.Redirect(o.redirectIfExists, fasthttp.StatusTemporaryRedirect) return true } - log.Debug().Msg("error handling") + log.Debug().Msg("Handling error") // Set the MIME type mimeType := o.getMimeTypeByExtension() @@ -175,7 +178,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } ctx.Response.Header.SetLastModified(o.BranchTimestamp) - log.Debug().Msg("response preparations") + log.Debug().Msg("Prepare response") // Write the response body to the original request var cacheBodyWriter bytes.Buffer @@ -193,11 +196,11 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, _, err = ctx.Write(cachedResponse.Body) } if err != nil { - fmt.Printf("Couldn't write body for \"%s\": %s\n", uri, err) + log.Error().Err(err).Msgf("Couldn't write body for %q", uri) html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } - log.Debug().Msg("response") + log.Debug().Msg("Sending response") if res != nil && res.Header.ContentLength() <= fileCacheSizeLimit && ctx.Err() == nil { cachedResponse.Exists = true From f72bbfd85fd8035a2a4dc216f6aeb9bc2fe19d0c Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 12 Aug 2022 05:24:05 +0200 Subject: [PATCH 151/320] Fix `just dev` (#121) - Use the correct log level command, since 876a53d9a245c11c58b61617052f7f2281d16358 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/121 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- Justfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index c824efa..03a7436 100644 --- a/Justfile +++ b/Justfile @@ -6,7 +6,8 @@ dev: export PAGES_DOMAIN=localhost.mock.directory export RAW_DOMAIN=raw.localhost.mock.directory export PORT=4430 - go run . --verbose + export LOG_LEVEL=trace + go run . build: CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ @@ -45,4 +46,4 @@ integration: go test -race -tags integration codeberg.org/codeberg/pages/integration/... integration-run TEST: - go test -race -tags integration -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... \ No newline at end of file + go test -race -tags integration -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... From 519259f459395073cca05f653c1d489f0da5d497 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 12 Aug 2022 06:14:39 +0200 Subject: [PATCH 152/320] publish docker images on tag and push to main (#122) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/122 --- .woodpecker.yml | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index b83a4dd..777c2af 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -26,7 +26,8 @@ pipeline: when: event: [ "pull_request", "push" ] - build-docker: + docker-dryrun: + group: compliant image: plugins/kaniko settings: dockerfile: Dockerfile @@ -47,14 +48,14 @@ pipeline: event: [ "tag" ] test: - image: a6543/golang_just group: test + image: a6543/golang_just commands: - just test integration-tests: - image: a6543/golang_just group: test + image: a6543/golang_just commands: - just integration environment: @@ -78,3 +79,31 @@ pipeline: - DRONE_COMMIT_REF=${CI_COMMIT_REF} when: event: [ "tag" ] + + docker-next: + image: plugins/kaniko + settings: + registry: codeberg.org + dockerfile: Dockerfile + repo: Codeberg/pages-server + tags: next + username: + from_secret: bot_user + password: + from_secret: bot_token + when: + event: [ "push" ] + + docker-tag: + image: plugins/kaniko + settings: + registry: codeberg.org + dockerfile: Dockerfile + repo: Codeberg/pages-server + tag: [ latest, "${CI_COMMIT_TAG}" ] + username: + from_secret: bot_user + password: + from_secret: bot_token + when: + event: [ "tag" ] From dc41a4caf4d6d078ee96626ae2174fe62af26cf4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@noreply.codeberg.org> Date: Fri, 12 Aug 2022 06:40:12 +0200 Subject: [PATCH 153/320] Add Support to Follow Symlinks and LFS (#114) close #79 close #80 close #91 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/114 --- cmd/flags.go | 13 ++++++++ cmd/main.go | 2 +- go.mod | 2 +- go.sum | 3 -- integration/get_test.go | 35 +++++++++++++++++++++ server/gitea/cache.go | 12 +++++++ server/gitea/client.go | 62 +++++++++++++++++++++---------------- server/handler_test.go | 2 +- server/upstream/helper.go | 4 +++ server/upstream/upstream.go | 2 +- 10 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 server/gitea/cache.go diff --git a/cmd/flags.go b/cmd/flags.go index 3c1ac99..8ac09ec 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -63,6 +63,19 @@ var ServeFlags = []cli.Flag{ // TODO: desc EnvVars: []string{"ENABLE_HTTP_SERVER"}, }, + // Server Options + &cli.BoolFlag{ + Name: "enable-lfs-support", + Usage: "enable lfs support, require gitea v1.17.0 as backend", + EnvVars: []string{"ENABLE_LFS_SUPPORT"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "enable-symlink-support", + Usage: "follow symlinks if enabled, require gitea v1.18.0 as backend", + EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, + Value: true, + }, &cli.StringFlag{ Name: "log-level", Value: "warn", diff --git a/cmd/main.go b/cmd/main.go index f57eb60..41809cb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -85,7 +85,7 @@ func Serve(ctx *cli.Context) error { // TODO: make this an MRU cache with a size limit fileResponseCache := cache.NewKeyValueCache() - giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken) + giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken, ctx.Bool("enable-symlink-support"), ctx.Bool("enable-lfs-support")) if err != nil { return fmt.Errorf("could not create new gitea client: %v", err) } diff --git a/go.mod b/go.mod index 64288de..479c328 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/go-acme/lego/v4 v4.5.3 github.com/joho/godotenv v1.4.0 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad + github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 github.com/valyala/fasthttp v1.31.0 @@ -92,7 +93,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.3.0 // indirect - github.com/rs/zerolog v1.27.0 // indirect github.com/russross/blackfriday/v2 v2.0.1 // indirect github.com/sacloud/libsacloud v1.36.2 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect diff --git a/go.sum b/go.sum index 4ac6eb6..23a58bc 100644 --- a/go.sum +++ b/go.sum @@ -327,7 +327,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -671,8 +670,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= diff --git a/integration/get_test.go b/integration/get_test.go index 191fa3f..6054e17 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -10,6 +10,7 @@ import ( "log" "net/http" "net/http/cookiejar" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -88,6 +89,34 @@ func TestGetNotFound(t *testing.T) { assert.EqualValues(t, 37, getSize(resp.Body)) } +func TestFollowSymlink(t *testing.T) { + log.Printf("=== TestFollowSymlink ===\n") + + resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/link") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + assert.EqualValues(t, "application/octet-stream", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "4", resp.Header.Get("Content-Length")) + body := getBytes(resp.Body) + assert.EqualValues(t, 4, len(body)) + assert.EqualValues(t, "abc\n", string(body)) +} + +func TestLFSSupport(t *testing.T) { + log.Printf("=== TestLFSSupport ===\n") + + resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/lfs.txt") + assert.NoError(t, err) + if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + t.FailNow() + } + body := strings.TrimSpace(string(getBytes(resp.Body))) + assert.EqualValues(t, 12, len(body)) + assert.EqualValues(t, "actual value", body) +} + func getTestHTTPSClient() *http.Client { cookieJar, _ := cookiejar.New(nil) return &http.Client{ @@ -101,6 +130,12 @@ func getTestHTTPSClient() *http.Client { } } +func getBytes(stream io.Reader) []byte { + buf := new(bytes.Buffer) + _, _ = buf.ReadFrom(stream) + return buf.Bytes() +} + func getSize(stream io.Reader) int { buf := new(bytes.Buffer) _, _ = buf.ReadFrom(stream) diff --git a/server/gitea/cache.go b/server/gitea/cache.go new file mode 100644 index 0000000..932ff3c --- /dev/null +++ b/server/gitea/cache.go @@ -0,0 +1,12 @@ +package gitea + +type FileResponse struct { + Exists bool + ETag []byte + MimeType string + Body []byte +} + +func (f FileResponse) IsEmpty() bool { + return len(f.Body) != 0 +} diff --git a/server/gitea/client.go b/server/gitea/client.go index 3b9ad6f..16cba84 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -7,11 +7,15 @@ import ( "strings" "time" + "github.com/rs/zerolog/log" "github.com/valyala/fasthttp" "github.com/valyala/fastjson" ) -const giteaAPIRepos = "/api/v1/repos/" +const ( + giteaAPIRepos = "/api/v1/repos/" + giteaObjectTypeHeader = "X-Gitea-Object-Type" +) var ErrorNotFound = errors.New("not found") @@ -21,13 +25,9 @@ type Client struct { fastClient *fasthttp.Client infoTimeout time.Duration contentTimeout time.Duration -} -type FileResponse struct { - Exists bool - ETag []byte - MimeType string - Body []byte + followSymlinks bool + supportLFS bool } // TODO: once golang v1.19 is min requirement, we can switch to 'JoinPath()' of 'net/url' package @@ -44,9 +44,7 @@ func joinURL(baseURL string, paths ...string) string { return baseURL + "/" + strings.Join(p, "/") } -func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } - -func NewClient(giteaRoot, giteaAPIToken string) (*Client, error) { +func NewClient(giteaRoot, giteaAPIToken string, followSymlinks, supportLFS bool) (*Client, error) { rootURL, err := url.Parse(giteaRoot) giteaRoot = strings.Trim(rootURL.String(), "/") @@ -56,29 +54,28 @@ func NewClient(giteaRoot, giteaAPIToken string) (*Client, error) { infoTimeout: 5 * time.Second, contentTimeout: 10 * time.Second, fastClient: getFastHTTPClient(), + + followSymlinks: followSymlinks, + supportLFS: supportLFS, }, err } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - url := joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref)) - res, err := client.do(client.contentTimeout, url) + resp, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) if err != nil { return nil, err } - - switch res.StatusCode() { - case fasthttp.StatusOK: - return res.Body(), nil - case fasthttp.StatusNotFound: - return nil, ErrorNotFound - default: - return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) - } + return resp.Body(), nil } -func (client *Client) ServeRawContent(uri string) (*fasthttp.Response, error) { - url := joinURL(client.giteaRoot, giteaAPIRepos, uri) - res, err := client.do(client.contentTimeout, url) +func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (*fasthttp.Response, error) { + var apiURL string + if client.supportLFS { + apiURL = joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "media", resource+"?ref="+url.QueryEscape(ref)) + } else { + apiURL = joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref)) + } + resp, err := client.do(client.contentTimeout, apiURL) if err != nil { return nil, err } @@ -87,13 +84,24 @@ func (client *Client) ServeRawContent(uri string) (*fasthttp.Response, error) { return nil, err } - switch res.StatusCode() { + switch resp.StatusCode() { case fasthttp.StatusOK: - return res, nil + objType := string(resp.Header.Peek(giteaObjectTypeHeader)) + log.Trace().Msgf("server raw content object: %s", objType) + if client.followSymlinks && objType == "symlink" { + // TODO: limit to 1000 chars if we switched to std + linkDest := strings.TrimSpace(string(resp.Body())) + log.Debug().Msgf("follow symlink from '%s' to '%s'", resource, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + } + + return resp, nil + case fasthttp.StatusNotFound: return nil, ErrorNotFound + default: - return nil, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + return nil, fmt.Errorf("unexpected status code '%d'", resp.StatusCode()) } } diff --git a/server/handler_test.go b/server/handler_test.go index 23d9af5..f9a721a 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -13,7 +13,7 @@ import ( func TestHandlerPerformance(t *testing.T) { giteaRoot := "https://codeberg.org" - giteaClient, _ := gitea.NewClient(giteaRoot, "") + giteaClient, _ := gitea.NewClient(giteaRoot, "", false, false) testHandler := Handler( []byte("codeberg.page"), []byte("raw.codeberg.org"), giteaClient, diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 5bbe833..0714dcd 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -67,6 +67,10 @@ func (o *Options) generateUri() string { return path.Join(o.TargetOwner, o.TargetRepo, "raw", o.TargetBranch, o.TargetPath) } +func (o *Options) generateUriClientArgs() (targetOwner, targetRepo, ref, resource string) { + return o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath +} + func (o *Options) timestamp() string { return strconv.FormatInt(o.BranchTimestamp.Unix(), 10) } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 4371e88..0e27727 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -83,7 +83,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + o.timestamp()); ok && !cachedValue.(gitea.FileResponse).IsEmpty() { cachedResponse = cachedValue.(gitea.FileResponse) } else { - res, err = giteaClient.ServeRawContent(uri) + res, err = giteaClient.ServeRawContent(o.generateUriClientArgs()) } log.Debug().Msg("Aquisting") From 88a217fbe6285039f77b903a9fdd1d53cd2dcbff Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 12 Aug 2022 06:55:35 +0200 Subject: [PATCH 154/320] docker images must be lowercase --- .woodpecker.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 777c2af..c9a9dbe 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -32,7 +32,6 @@ pipeline: settings: dockerfile: Dockerfile no_push: true - repo: Codeberg/pages-server tags: latest when: event: [ "pull_request", "push" ] @@ -85,7 +84,7 @@ pipeline: settings: registry: codeberg.org dockerfile: Dockerfile - repo: Codeberg/pages-server + repo: codeberg/pages-server tags: next username: from_secret: bot_user @@ -99,7 +98,7 @@ pipeline: settings: registry: codeberg.org dockerfile: Dockerfile - repo: Codeberg/pages-server + repo: codeberg/pages-server tag: [ latest, "${CI_COMMIT_TAG}" ] username: from_secret: bot_user From 392c6ae45290de0ff278416f5f7cfead7d13e0a2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 12 Aug 2022 07:02:24 +0200 Subject: [PATCH 155/320] full-name --- .woodpecker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index c9a9dbe..2674e9b 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -84,7 +84,7 @@ pipeline: settings: registry: codeberg.org dockerfile: Dockerfile - repo: codeberg/pages-server + repo: codeberg.org/codeberg/pages-server tags: next username: from_secret: bot_user @@ -98,7 +98,7 @@ pipeline: settings: registry: codeberg.org dockerfile: Dockerfile - repo: codeberg/pages-server + repo: codeberg.org/codeberg/pages-server tag: [ latest, "${CI_COMMIT_TAG}" ] username: from_secret: bot_user From 1ae50735a1d50c01cd2149334c9fd756f5390fd1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 13 Aug 2022 18:03:31 +0200 Subject: [PATCH 156/320] Add host to handler logging (#123) - Add the host to the Handler's logging fields, so you don't just see the path, but also which domain was being requested. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/123 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- server/handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/handler.go b/server/handler.go index 50fbcc5..cd67aa7 100644 --- a/server/handler.go +++ b/server/handler.go @@ -25,7 +25,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, ) func(ctx *fasthttp.RequestCtx) { return func(ctx *fasthttp.RequestCtx) { - log := log.With().Str("Handler", string(ctx.Request.Header.RequestURI())).Logger() + log := log.With().Strs("Handler", []string{string(ctx.Request.Host()), string(ctx.Request.Header.RequestURI())}).Logger() ctx.Response.Header.Set("Server", "CodebergPages/"+version.Version) From 8f2699407d5c452e04bd0de8bbb367a60b4a88b8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 13 Sep 2022 23:06:31 +0200 Subject: [PATCH 157/320] Make verbose checks in tryBranch (#127) - It's likely that the tryBranch is returning false when it should be returning true, make these logs more verbose so they show up on production logs. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/127 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/handler.go b/server/handler.go index cd67aa7..ed06659 100644 --- a/server/handler.go +++ b/server/handler.go @@ -85,7 +85,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // also disallow search indexing and add a Link header to the canonical URL. tryBranch := func(log zerolog.Logger, repo, branch string, path []string, canonicalLink string) bool { if repo == "" { - log.Debug().Msg("tryBranch: repo is empty") + log.Warn().Msg("tryBranch: repo is empty") return false } @@ -96,7 +96,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Check if the branch exists, otherwise treat it as a file path branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch, branchTimestampCache) if branchTimestampResult == nil { - log.Debug().Msg("tryBranch: branch doesn't exist") + log.Warn().Msg("tryBranch: branch doesn't exist") return false } From 2a730b2439cae5ebd17d90e38b4ff27e4154a651 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 13 Sep 2022 23:26:45 +0200 Subject: [PATCH 158/320] Update README (#128) - Update readme accordingly to the https://codeberg.org/Codeberg/pages-server/commit/876a53d9a245c11c58b61617052f7f2281d16358 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/128 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 700f279..c4758ac 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. -- `DEBUG` (default: false): Set this to true to enable debug logging. +- `LOG_LEVEL` (default: warn): Set this to specify the level of logging. ## Contributing to the development @@ -106,4 +106,4 @@ run `just dev` now this pages should work: - https://magiclike.localhost.mock.directory:4430/ - https://momar.localhost.mock.directory:4430/ci-testing/ - - https://momar.localhost.mock.directory:4430/pag/@master/ \ No newline at end of file + - https://momar.localhost.mock.directory:4430/pag/@master/ From 091e6c8ed9f737b3b84a3610c5836953677d122c Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 18 Sep 2022 16:13:27 +0200 Subject: [PATCH 159/320] Add explicit logging in `GetBranchTimestamp` (#130) - Logs are currently indicating that it's returning `nil` in valid scenarios, therefor this patch adds extra logging in this code to better understand what it is doing in this function. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/130 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/handler.go | 2 +- server/upstream/helper.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/server/handler.go b/server/handler.go index ed06659..fb8b419 100644 --- a/server/handler.go +++ b/server/handler.go @@ -296,7 +296,7 @@ func Handler(mainDomainSuffix, rawDomain []byte, return } - log.Info().Msg("tryBranch, now trying upstream 7 %s") + log.Info().Msg("tryBranch, now trying upstream 7") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, canonicalDomainCache, branchTimestampCache, fileResponseCache) diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 0714dcd..28f4474 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -9,6 +9,7 @@ import ( "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/gitea" + "github.com/rs/zerolog/log" ) type branchTimestamp struct { @@ -19,10 +20,13 @@ type branchTimestamp struct { // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string, branchTimestampCache cache.SetGetKey) *branchTimestamp { + log := log.With().Strs("BranchInfo", []string{owner, repo, branch}).Logger() if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { if result == nil { + log.Debug().Msg("branchTimestampCache found item, but result is empty") return nil } + log.Debug().Msg("branchTimestampCache found item, returning result") return result.(*branchTimestamp) } result := &branchTimestamp{ @@ -32,16 +36,20 @@ func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string, b // Get default branch defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(owner, repo) if err != nil { + log.Err(err).Msg("Could't fetch default branch from repository") _ = branchTimestampCache.Set(owner+"/"+repo+"/", nil, defaultBranchCacheTimeout) return nil } + log.Debug().Msg("Succesfully fetched default branch from Gitea") result.Branch = defaultBranch } timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(owner, repo, result.Branch) if err != nil { + log.Err(err).Msg("Could not get latest commit's timestamp from branch") return nil } + log.Debug().Msg("Succesfully fetched latest commit's timestamp from branch, adding to cache") result.Timestamp = timestamp _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, branchExistenceCacheTimeout) return result From df2228b6d59b175df87b8849391b4a8d317e7eb3 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 10 Oct 2022 23:25:21 +0200 Subject: [PATCH 160/320] ci: let tag run pipeline --- .woodpecker.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 2674e9b..198648f 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,5 +1,3 @@ -branches: main - pipeline: # use vendor to cache dependencies vendor: From b9e9f1420954d1a352f7e9e7e799b160bd224eb1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 10 Oct 2022 23:27:33 +0200 Subject: [PATCH 161/320] use codeberg.org/6543/docker-images/golang_just Signed-off-by: 6543 <6543@obermui.de> --- .woodpecker.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 198648f..db51eba 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -17,7 +17,7 @@ pipeline: build: group: compliant - image: a6543/golang_just + image: codeberg.org/6543/docker-images/golang_just commands: - go version - just build @@ -37,7 +37,7 @@ pipeline: build-tag: group: compliant - image: a6543/golang_just + image: codeberg.org/6543/docker-images/golang_just commands: - go version - just build-tag ${CI_COMMIT_TAG##v} @@ -46,13 +46,13 @@ pipeline: test: group: test - image: a6543/golang_just + image: codeberg.org/6543/docker-images/golang_just commands: - just test integration-tests: group: test - image: a6543/golang_just + image: codeberg.org/6543/docker-images/golang_just commands: - just integration environment: From bf9a08e1fd4107667dc6816e1df0d0ec2c0acbe9 Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 7 Nov 2022 16:27:37 +0100 Subject: [PATCH 162/320] Fatal on ACME Client creation failure (#133) - For production(*cough* Codeberg *cough*), it's important to not use mock certs. So fail right from the start if this is the case and not try to "handle it gracefully", as it would break production. - Resolves #131 CC @6543 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/133 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/certificates/certificates.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 8944468..2f59fb4 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -414,7 +414,7 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * acmeClient, err = lego.NewClient(acmeConfig) if err != nil { - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { From 91b54bef29ca9e7d3394fad1e44fa4b6f795035c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 7 Nov 2022 23:09:41 +0100 Subject: [PATCH 163/320] add newline --- server/upstream/upstream.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 0e27727..61c90de 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -85,6 +85,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, } else { res, err = giteaClient.ServeRawContent(o.generateUriClientArgs()) } + log.Debug().Msg("Aquisting") // Handle errors @@ -158,6 +159,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, ctx.Redirect(o.redirectIfExists, fasthttp.StatusTemporaryRedirect) return true } + log.Debug().Msg("Handling error") // Set the MIME type @@ -200,6 +202,7 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) return true } + log.Debug().Msg("Sending response") if res != nil && res.Header.ContentLength() <= fileCacheSizeLimit && ctx.Err() == nil { From 8e67d28c4fddfc0b4f4f0dd5db1e60e4d80cf56a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 11 Nov 2022 23:51:45 +0100 Subject: [PATCH 164/320] Add editorconfig, fix files and lint via ci --- .ecrc | 9 +++++++++ .editorconfig | 17 +++++++++++++++++ .woodpecker.yml | 4 ++++ Dockerfile | 4 ++-- Justfile | 1 + README.md | 8 +++----- haproxy-sni/dhparam.pem | 2 +- haproxy-sni/docker-compose.yml | 2 +- html/404.html | 6 +++--- 9 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 .ecrc create mode 100644 .editorconfig diff --git a/.ecrc b/.ecrc new file mode 100644 index 0000000..d9ee788 --- /dev/null +++ b/.ecrc @@ -0,0 +1,9 @@ +{ + "Exclude": [ + ".git", + "go.mod", "go.sum", + "vendor", + "LICENSE", + "_test.go" + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..354a828 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +tab_width = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false +indent_size = 1 diff --git a/.woodpecker.yml b/.woodpecker.yml index db51eba..5c9a7fd 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -15,6 +15,10 @@ pipeline: - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - golangci-lint run --timeout 5m --build-tags integration + editor-config: + group: compliant + image: mstruebing/editorconfig-checker + build: group: compliant image: codeberg.org/6543/docker-images/golang_just diff --git a/Dockerfile b/Dockerfile index 71dd236..904d6f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,5 +11,5 @@ COPY --from=build /workspace/pages /pages COPY --from=build \ /etc/ssl/certs/ca-certificates.crt \ /etc/ssl/certs/ca-certificates.crt - -ENTRYPOINT ["/pages"] \ No newline at end of file + +ENTRYPOINT ["/pages"] diff --git a/Justfile b/Justfile index 03a7436..f7ea414 100644 --- a/Justfile +++ b/Justfile @@ -18,6 +18,7 @@ build-tag VERSION: lint: tool-golangci tool-gofumpt [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ golangci-lint run --timeout 5m --build-tags integration + # TODO: run editorconfig-checker fmt: tool-gofumpt gofumpt -w --extra . diff --git a/README.md b/README.md index c4758ac..50cdc3c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ It is suitable to be deployed by other Gitea instances, too, to offer static pag **End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview) and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/). - ## Quickstart This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. @@ -20,17 +19,16 @@ first line will be the canonical domain/URL; all other occurrences will be redir 2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else. If the branch name contains slash characters, you need to replace "/" in the branch name to "~"): - `www.example.org. IN CNAME main.pages.example.codeberg.page.` + `www.example.org. IN CNAME main.pages.example.codeberg.page.` 3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT record that points to your repo (just like the CNAME record): - `example.org IN ALIAS codeberg.page.` - `example.org IN TXT main.pages.example.codeberg.page.` + `example.org IN ALIAS codeberg.page.` + `example.org IN TXT main.pages.example.codeberg.page.` Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. - ## Deployment **Warning: Some Caveats Apply** diff --git a/haproxy-sni/dhparam.pem b/haproxy-sni/dhparam.pem index 088f967..9b182b7 100644 --- a/haproxy-sni/dhparam.pem +++ b/haproxy-sni/dhparam.pem @@ -5,4 +5,4 @@ MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== ------END DH PARAMETERS----- \ No newline at end of file +-----END DH PARAMETERS----- diff --git a/haproxy-sni/docker-compose.yml b/haproxy-sni/docker-compose.yml index 4dd8677..66ff52b 100644 --- a/haproxy-sni/docker-compose.yml +++ b/haproxy-sni/docker-compose.yml @@ -19,4 +19,4 @@ services: volumes: - ./pages-www:/srv:ro - ./pages.Caddyfile:/etc/caddy/Caddyfile:ro - + diff --git a/html/404.html b/html/404.html index b7ec96e..21d968e 100644 --- a/html/404.html +++ b/html/404.html @@ -23,11 +23,11 @@

- Page not found! + Page not found!

- Sorry, but this page couldn't be found or is inaccessible (%status).
- We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs! + Sorry, but this page couldn't be found or is inaccessible (%status).
+ We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs!
From 69eabb248a26e33b65732604267c486fd0daee84 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 00:23:45 +0100 Subject: [PATCH 165/320] CI publish next only on default branch --- .woodpecker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker.yml b/.woodpecker.yml index 5c9a7fd..20254fe 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -94,6 +94,7 @@ pipeline: from_secret: bot_token when: event: [ "push" ] + branch: ${CI_REPO_DEFAULT_BRANCH} docker-tag: image: plugins/kaniko From b9966487f6f7a9fc88ae285c7350917e455f27a0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 20:37:20 +0100 Subject: [PATCH 166/320] switch to std http implementation instead of fasthttp (#106) close #100 close #109 close #113 close #28 close #63 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/106 --- cmd/main.go | 41 ++-- go.mod | 17 +- go.sum | 33 +-- html/404.html | 8 +- html/error.go | 47 +++-- html/error.html | 38 ++++ html/html.go | 5 +- integration/get_test.go | 33 ++- server/certificates/certificates.go | 36 ++-- server/context/context.go | 62 ++++++ server/database/mock.go | 2 +- server/dns/const.go | 6 - server/dns/dns.go | 4 + server/gitea/cache.go | 112 +++++++++- server/gitea/client.go | 308 ++++++++++++++++++++-------- server/gitea/client_test.go | 23 --- server/gitea/fasthttp.go | 15 -- server/handler.go | 186 +++++++++-------- server/handler_test.go | 36 ++-- server/helpers.go | 8 +- server/setup.go | 54 ++--- server/try.go | 21 +- server/upstream/const.go | 24 --- server/upstream/domains.go | 10 + server/upstream/helper.go | 74 ++----- server/upstream/upstream.go | 194 +++++++++--------- server/utils/utils.go | 8 +- server/utils/utils_test.go | 6 +- 28 files changed, 827 insertions(+), 584 deletions(-) create mode 100644 html/error.html create mode 100644 server/context/context.go delete mode 100644 server/dns/const.go delete mode 100644 server/gitea/client_test.go delete mode 100644 server/gitea/fasthttp.go delete mode 100644 server/upstream/const.go diff --git a/cmd/main.go b/cmd/main.go index 41809cb..a3a61e1 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,12 +1,12 @@ package cmd import ( - "bytes" "context" "crypto/tls" "errors" "fmt" "net" + "net/http" "os" "strings" "time" @@ -24,15 +24,15 @@ import ( // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. // TODO: make it a flag -var AllowedCorsDomains = [][]byte{ - []byte("fonts.codeberg.org"), - []byte("design.codeberg.org"), +var AllowedCorsDomains = []string{ + "fonts.codeberg.org", + "design.codeberg.org", } // BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. // TODO: Make it a flag too -var BlacklistedPaths = [][]byte{ - []byte("/.well-known/acme-challenge/"), +var BlacklistedPaths = []string{ + "/.well-known/acme-challenge/", } // Serve sets up and starts the web server. @@ -47,7 +47,7 @@ func Serve(ctx *cli.Context) error { giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") giteaAPIToken := ctx.String("gitea-api-token") rawDomain := ctx.String("raw-domain") - mainDomainSuffix := []byte(ctx.String("pages-domain")) + mainDomainSuffix := ctx.String("pages-domain") rawInfoPage := ctx.String("raw-info-page") listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) enableHTTPServer := ctx.Bool("enable-http-server") @@ -65,12 +65,12 @@ func Serve(ctx *cli.Context) error { allowedCorsDomains := AllowedCorsDomains if len(rawDomain) != 0 { - allowedCorsDomains = append(allowedCorsDomains, []byte(rawDomain)) + allowedCorsDomains = append(allowedCorsDomains, rawDomain) } // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash - if !bytes.HasPrefix(mainDomainSuffix, []byte{'.'}) { - mainDomainSuffix = append([]byte{'.'}, mainDomainSuffix...) + if !strings.HasPrefix(mainDomainSuffix, ".") { + mainDomainSuffix = "." + mainDomainSuffix } keyCache := cache.NewKeyValueCache() @@ -79,26 +79,22 @@ func Serve(ctx *cli.Context) error { canonicalDomainCache := cache.NewKeyValueCache() // dnsLookupCache stores DNS lookups for custom domains dnsLookupCache := cache.NewKeyValueCache() - // branchTimestampCache stores branch timestamps for faster cache checking - branchTimestampCache := cache.NewKeyValueCache() - // fileResponseCache stores responses from the Gitea server - // TODO: make this an MRU cache with a size limit - fileResponseCache := cache.NewKeyValueCache() + // clientResponseCache stores responses from the Gitea server + clientResponseCache := cache.NewKeyValueCache() - giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken, ctx.Bool("enable-symlink-support"), ctx.Bool("enable-lfs-support")) + giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken, clientResponseCache, ctx.Bool("enable-symlink-support"), ctx.Bool("enable-lfs-support")) if err != nil { return fmt.Errorf("could not create new gitea client: %v", err) } // Create handler based on settings - handler := server.Handler(mainDomainSuffix, []byte(rawDomain), + httpsHandler := server.Handler(mainDomainSuffix, rawDomain, giteaClient, giteaRoot, rawInfoPage, BlacklistedPaths, allowedCorsDomains, - dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache) + dnsLookupCache, canonicalDomainCache) - fastServer := server.SetupServer(handler) - httpServer := server.SetupHTTPACMEChallengeServer(challengeCache) + httpHandler := server.SetupHTTPACMEChallengeServer(challengeCache) // Setup listener and TLS log.Info().Msgf("Listening on https://%s", listeningAddress) @@ -138,7 +134,7 @@ func Serve(ctx *cli.Context) error { if enableHTTPServer { go func() { log.Info().Msg("Start HTTP server listening on :80") - err := httpServer.ListenAndServe("[::]:80") + err := http.ListenAndServe("[::]:80", httpHandler) if err != nil { log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") } @@ -147,8 +143,7 @@ func Serve(ctx *cli.Context) error { // Start the web fastServer log.Info().Msgf("Start listening on %s", listener.Addr()) - err = fastServer.Serve(listener) - if err != nil { + if err := http.Serve(listener, httpsHandler); err != nil { log.Panic().Err(err).Msg("Couldn't start fastServer") } diff --git a/go.mod b/go.mod index 479c328..77ed762 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,9 @@ module codeberg.org/codeberg/pages -go 1.18 +go 1.19 require ( + code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 @@ -11,8 +12,6 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 - github.com/valyala/fasthttp v1.31.0 - github.com/valyala/fastjson v1.6.3 ) require ( @@ -31,7 +30,6 @@ require ( github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect - github.com/andybalholm/brotli v1.0.2 // indirect github.com/aws/aws-sdk-go v1.39.0 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect @@ -39,6 +37,7 @@ require ( github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davidmz/go-pageant v1.0.2 // indirect github.com/deepmap/oapi-codegen v1.6.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dnsimple/dnsimple-go v0.70.1 // indirect @@ -46,6 +45,7 @@ require ( github.com/fatih/structs v1.1.0 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/go-errors/errors v1.0.1 // indirect + github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect github.com/gofrs/uuid v3.2.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect @@ -57,13 +57,13 @@ require ( github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/jarcoal/httpmock v1.0.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.7 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect - github.com/klauspost/compress v1.13.4 // indirect github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect @@ -104,15 +104,14 @@ require ( github.com/spf13/cast v1.3.1 // indirect github.com/stretchr/objx v0.3.0 // indirect github.com/transip/gotransip/v6 v6.6.1 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect github.com/vultr/govultr/v2 v2.7.1 // indirect go.opencensus.io v0.22.3 // indirect go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect - golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect diff --git a/go.sum b/go.sum index 23a58bc..a44001c 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa h1:OVwgYrY6vr6gWZvgnmevFhtL0GVA4HKaFOhD+joPoNk= +code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -66,8 +68,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbxuNdz3FeyvHFnRjmo= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= -github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= -github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -106,6 +106,8 @@ github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= +github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpAMecnw= github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -136,6 +138,8 @@ github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJ github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= +github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -182,7 +186,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -243,6 +246,9 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -282,8 +288,6 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcM github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -494,15 +498,9 @@ github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.31.0 h1:lrauRLII19afgCs2fnWRJ4M5IkV0lo2FqA61uGkNBfE= -github.com/valyala/fasthttp v1.31.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= -github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc= -github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= github.com/vultr/govultr/v2 v2.7.1 h1:uF9ERet++Gb+7Cqs3p1P6b6yebeaZqVd7t5P2uZCaJU= @@ -539,8 +537,10 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -604,8 +604,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -667,12 +667,13 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/html/404.html b/html/404.html index 21d968e..7c721b5 100644 --- a/html/404.html +++ b/html/404.html @@ -3,7 +3,7 @@ - %status + %status% @@ -23,11 +23,11 @@

- Page not found! + Page not found!

- Sorry, but this page couldn't be found or is inaccessible (%status).
- We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs! + Sorry, but this page couldn't be found or is inaccessible (%status%).
+ We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs!
diff --git a/html/error.go b/html/error.go index 325dada..826c42b 100644 --- a/html/error.go +++ b/html/error.go @@ -1,24 +1,45 @@ package html import ( - "bytes" + "net/http" "strconv" + "strings" - "github.com/valyala/fasthttp" + "codeberg.org/codeberg/pages/server/context" ) -// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body, with "%status" replaced -// with the provided status code. -func ReturnErrorPage(ctx *fasthttp.RequestCtx, code int) { - ctx.Response.SetStatusCode(code) - ctx.Response.Header.SetContentType("text/html; charset=utf-8") - message := fasthttp.StatusMessage(code) - if code == fasthttp.StatusMisdirectedRequest { - message += " - domain not specified in .domains file" +// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body, +// with "%status%" and %message% replaced with the provided statusCode and msg +func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) { + ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8") + ctx.RespWriter.WriteHeader(statusCode) + + if msg == "" { + msg = errorBody(statusCode) + } else { + // TODO: use template engine + msg = strings.ReplaceAll(strings.ReplaceAll(ErrorPage, "%message%", msg), "%status%", http.StatusText(statusCode)) } - if code == fasthttp.StatusFailedDependency { + + _, _ = ctx.RespWriter.Write([]byte(msg)) +} + +func errorMessage(statusCode int) string { + message := http.StatusText(statusCode) + + switch statusCode { + case http.StatusMisdirectedRequest: + message += " - domain not specified in .domains file" + case http.StatusFailedDependency: message += " - target repo/branch doesn't exist or is private" } - // TODO: use template engine? - ctx.Response.SetBody(bytes.ReplaceAll(NotFoundPage, []byte("%status"), []byte(strconv.Itoa(code)+" "+message))) + + return message +} + +// TODO: use template engine +func errorBody(statusCode int) string { + return strings.ReplaceAll(NotFoundPage, + "%status%", + strconv.Itoa(statusCode)+" "+errorMessage(statusCode)) } diff --git a/html/error.html b/html/error.html new file mode 100644 index 0000000..f1975f7 --- /dev/null +++ b/html/error.html @@ -0,0 +1,38 @@ + + + + + + %status% + + + + + + + + + +

+ %status%! +

+
+ Sorry, but this page couldn't be served.
+ We got an "%message%"
+ We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs! +
+ + + Static pages made easy - Codeberg Pages + + + diff --git a/html/html.go b/html/html.go index d223e15..a76ce59 100644 --- a/html/html.go +++ b/html/html.go @@ -3,4 +3,7 @@ package html import _ "embed" //go:embed 404.html -var NotFoundPage []byte +var NotFoundPage string + +//go:embed error.html +var ErrorPage string diff --git a/integration/get_test.go b/integration/get_test.go index 6054e17..8794651 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -25,7 +25,7 @@ func TestGetRedirect(t *testing.T) { t.FailNow() } assert.EqualValues(t, "https://www.cabr2.de/", resp.Header.Get("Location")) - assert.EqualValues(t, 0, getSize(resp.Body)) + assert.EqualValues(t, `Temporary Redirect.`, strings.TrimSpace(string(getBytes(resp.Body)))) } func TestGetContent(t *testing.T) { @@ -44,12 +44,13 @@ func TestGetContent(t *testing.T) { // specify branch resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/") assert.NoError(t, err) - if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + if !assert.NotNil(t, resp) { t.FailNow() } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.True(t, getSize(resp.Body) > 1000) - assert.Len(t, resp.Header.Get("ETag"), 42) + assert.Len(t, resp.Header.Get("ETag"), 44) // access branch name contains '/' resp, err = getTestHTTPSClient().Get("https://blumia.localhost.mock.directory:4430/pages-server-integration-tests/@docs~main/") @@ -59,7 +60,7 @@ func TestGetContent(t *testing.T) { } assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.True(t, getSize(resp.Body) > 100) - assert.Len(t, resp.Header.Get("ETag"), 42) + assert.Len(t, resp.Header.Get("ETag"), 44) // TODO: test get of non cachable content (content size > fileCacheSizeLimit) } @@ -68,9 +69,10 @@ func TestCustomDomain(t *testing.T) { log.Println("=== TestCustomDomain ===") resp, err := getTestHTTPSClient().Get("https://mock-pages.codeberg-test.org:4430/README.md") assert.NoError(t, err) - if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + if !assert.NotNil(t, resp) { t.FailNow() } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) assert.EqualValues(t, "text/markdown; charset=utf-8", resp.Header.Get("Content-Type")) assert.EqualValues(t, "106", resp.Header.Get("Content-Length")) assert.EqualValues(t, 106, getSize(resp.Body)) @@ -81,9 +83,10 @@ func TestGetNotFound(t *testing.T) { // test custom not found pages resp, err := getTestHTTPSClient().Get("https://crystal.localhost.mock.directory:4430/pages-404-demo/blah") assert.NoError(t, err) - if !assert.EqualValues(t, http.StatusNotFound, resp.StatusCode) { + if !assert.NotNil(t, resp) { t.FailNow() } + assert.EqualValues(t, http.StatusNotFound, resp.StatusCode) assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) assert.EqualValues(t, "37", resp.Header.Get("Content-Length")) assert.EqualValues(t, 37, getSize(resp.Body)) @@ -94,9 +97,10 @@ func TestFollowSymlink(t *testing.T) { resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/link") assert.NoError(t, err) - if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + if !assert.NotNil(t, resp) { t.FailNow() } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) assert.EqualValues(t, "application/octet-stream", resp.Header.Get("Content-Type")) assert.EqualValues(t, "4", resp.Header.Get("Content-Length")) body := getBytes(resp.Body) @@ -109,14 +113,27 @@ func TestLFSSupport(t *testing.T) { resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/lfs.txt") assert.NoError(t, err) - if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { + if !assert.NotNil(t, resp) { t.FailNow() } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) body := strings.TrimSpace(string(getBytes(resp.Body))) assert.EqualValues(t, 12, len(body)) assert.EqualValues(t, "actual value", body) } +func TestGetOptions(t *testing.T) { + log.Println("=== TestGetOptions ===") + req, _ := http.NewRequest(http.MethodOptions, "https://mock-pages.codeberg-test.org:4430/README.md", nil) + resp, err := getTestHTTPSClient().Do(req) + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusNoContent, resp.StatusCode) + assert.EqualValues(t, "GET, HEAD, OPTIONS", resp.Header.Get("Allow")) +} + func getTestHTTPSClient() *http.Client { cookieJar, _ := cookiejar.New(nil) return &http.Client{ diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 2f59fb4..429ab23 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -36,7 +36,7 @@ import ( ) // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. -func TLSConfig(mainDomainSuffix []byte, +func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, dnsProvider string, acmeUseRateLimits bool, @@ -47,7 +47,6 @@ func TLSConfig(mainDomainSuffix []byte, // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { sni := strings.ToLower(strings.TrimSpace(info.ServerName)) - sniBytes := []byte(sni) if len(sni) < 1 { return nil, errors.New("missing sni") } @@ -69,23 +68,20 @@ func TLSConfig(mainDomainSuffix []byte, } targetOwner := "" - if bytes.HasSuffix(sniBytes, mainDomainSuffix) || bytes.Equal(sniBytes, mainDomainSuffix[1:]) { + if strings.HasSuffix(sni, mainDomainSuffix) || strings.EqualFold(sni, mainDomainSuffix[1:]) { // deliver default certificate for the main domain (*.codeberg.page) - sniBytes = mainDomainSuffix - sni = string(sniBytes) + sni = mainDomainSuffix } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(sni, string(mainDomainSuffix), dnsLookupCache) + targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(sni, mainDomainSuffix, dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs - sniBytes = mainDomainSuffix - sni = string(sniBytes) + sni = mainDomainSuffix } else { _, _ = targetRepo, targetBranch - _, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, sni, string(mainDomainSuffix), canonicalDomainCache) + _, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, sni, mainDomainSuffix, canonicalDomainCache) if !valid { - sniBytes = mainDomainSuffix - sni = string(sniBytes) + sni = mainDomainSuffix } } } @@ -98,9 +94,9 @@ func TLSConfig(mainDomainSuffix []byte, var tlsCertificate tls.Certificate var err error var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sniBytes, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); !ok { + if tlsCertificate, ok = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); !ok { // request a new certificate - if bytes.Equal(sniBytes, mainDomainSuffix) { + if strings.EqualFold(sni, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } @@ -192,7 +188,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { // parse certificate from database res, err := certDB.Get(string(sni)) if err != nil { @@ -208,7 +204,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs } // TODO: document & put into own function - if !bytes.Equal(sni, mainDomainSuffix) { + if !strings.EqualFold(sni, mainDomainSuffix) { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { panic(err) @@ -239,7 +235,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix []byte, dnsProvider string, acmeUs var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider string, mainDomainSuffix []byte, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -252,7 +248,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re time.Sleep(100 * time.Millisecond) _, working = obtainLocks.Load(name) } - cert, ok := retrieveCertFromDB([]byte(name), mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + cert, ok := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) if !ok { return tls.Certificate{}, errors.New("certificate failed in synchronous request") } @@ -405,7 +401,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce return myAcmeConfig, nil } -func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { +func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits mainCertBytes, err := certDB.Get(string(mainDomainSuffix)) if err != nil { @@ -460,7 +456,7 @@ func SetupCertificates(mainDomainSuffix []byte, dnsProvider string, acmeConfig * return nil } -func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix []byte, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { +func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { for { // clean up expired certs now := time.Now() @@ -468,7 +464,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi keyDatabaseIterator := certDB.Items() key, resBytes, err := keyDatabaseIterator.Next() for err == nil { - if !bytes.Equal(key, mainDomainSuffix) { + if !strings.EqualFold(string(key), mainDomainSuffix) { resGob := bytes.NewBuffer(resBytes) resDec := gob.NewDecoder(resGob) res := &certificate.Resource{} diff --git a/server/context/context.go b/server/context/context.go new file mode 100644 index 0000000..be01df0 --- /dev/null +++ b/server/context/context.go @@ -0,0 +1,62 @@ +package context + +import ( + stdContext "context" + "net/http" +) + +type Context struct { + RespWriter http.ResponseWriter + Req *http.Request + StatusCode int +} + +func New(w http.ResponseWriter, r *http.Request) *Context { + return &Context{ + RespWriter: w, + Req: r, + StatusCode: http.StatusOK, + } +} + +func (c *Context) Context() stdContext.Context { + if c.Req != nil { + return c.Req.Context() + } + return stdContext.Background() +} + +func (c *Context) Response() *http.Response { + if c.Req != nil && c.Req.Response != nil { + return c.Req.Response + } + return nil +} + +func (c *Context) String(raw string, status ...int) { + code := http.StatusOK + if len(status) != 0 { + code = status[0] + } + c.RespWriter.WriteHeader(code) + _, _ = c.RespWriter.Write([]byte(raw)) +} + +func (c *Context) IsMethod(m string) bool { + return c.Req.Method == m +} + +func (c *Context) Redirect(uri string, statusCode int) { + http.Redirect(c.RespWriter, c.Req, uri, statusCode) +} + +// Path returns requested path. +// +// The returned bytes are valid until your request handler returns. +func (c *Context) Path() string { + return c.Req.URL.Path +} + +func (c *Context) Host() string { + return c.Req.URL.Host +} diff --git a/server/database/mock.go b/server/database/mock.go index e6c1b5a..dfe2316 100644 --- a/server/database/mock.go +++ b/server/database/mock.go @@ -28,7 +28,7 @@ func (p tmpDB) Put(name string, cert *certificate.Resource) error { func (p tmpDB) Get(name string) (*certificate.Resource, error) { cert, has := p.intern.Get(name) if !has { - return nil, fmt.Errorf("cert for '%s' not found", name) + return nil, fmt.Errorf("cert for %q not found", name) } return cert.(*certificate.Resource), nil } diff --git a/server/dns/const.go b/server/dns/const.go deleted file mode 100644 index bb2413b..0000000 --- a/server/dns/const.go +++ /dev/null @@ -1,6 +0,0 @@ -package dns - -import "time" - -// lookupCacheTimeout specifies the timeout for the DNS lookup cache. -var lookupCacheTimeout = 15 * time.Minute diff --git a/server/dns/dns.go b/server/dns/dns.go index dc759b0..818e29a 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -3,10 +3,14 @@ package dns import ( "net" "strings" + "time" "codeberg.org/codeberg/pages/server/cache" ) +// lookupCacheTimeout specifies the timeout for the DNS lookup cache. +var lookupCacheTimeout = 15 * time.Minute + // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 932ff3c..b11a370 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -1,12 +1,116 @@ package gitea +import ( + "bytes" + "fmt" + "io" + "net/http" + "time" + + "github.com/rs/zerolog/log" + + "codeberg.org/codeberg/pages/server/cache" +) + +const ( + // defaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. + defaultBranchCacheTimeout = 15 * time.Minute + + // branchExistenceCacheTimeout specifies the timeout for the branch timestamp & existence cache. It should be shorter + // than fileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be + // picked up faster, while still allowing the content to be cached longer if nothing changes. + branchExistenceCacheTimeout = 5 * time.Minute + + // fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending + // on your available memory. + // TODO: move as option into cache interface + fileCacheTimeout = 5 * time.Minute + + // fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. + fileCacheSizeLimit = int64(1000 * 1000) +) + type FileResponse struct { - Exists bool - ETag []byte - MimeType string - Body []byte + Exists bool + IsSymlink bool + ETag string + MimeType string + Body []byte } func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } + +func (f FileResponse) createHttpResponse(cacheKey string) (http.Header, int) { + header := make(http.Header) + var statusCode int + + if f.Exists { + statusCode = http.StatusOK + } else { + statusCode = http.StatusNotFound + } + + if f.IsSymlink { + header.Set(giteaObjectTypeHeader, objTypeSymlink) + } + header.Set(ETagHeader, f.ETag) + header.Set(ContentTypeHeader, f.MimeType) + header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body))) + header.Set(PagesCacheIndicatorHeader, "true") + + log.Trace().Msgf("fileCache for %q used", cacheKey) + return header, statusCode +} + +type BranchTimestamp struct { + Branch string + Timestamp time.Time + notFound bool +} + +type writeCacheReader struct { + originalReader io.ReadCloser + buffer *bytes.Buffer + rileResponse *FileResponse + cacheKey string + cache cache.SetGetKey + hasError bool +} + +func (t *writeCacheReader) Read(p []byte) (n int, err error) { + n, err = t.originalReader.Read(p) + if err != nil { + log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) + t.hasError = true + } else if n > 0 { + _, _ = t.buffer.Write(p[:n]) + } + return +} + +func (t *writeCacheReader) Close() error { + if !t.hasError { + fc := *t.rileResponse + fc.Body = t.buffer.Bytes() + _ = t.cache.Set(t.cacheKey, fc, fileCacheTimeout) + } + log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, !t.hasError) + return t.originalReader.Close() +} + +func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.SetGetKey, cacheKey string) io.ReadCloser { + if r == nil || cache == nil || cacheKey == "" { + log.Error().Msg("could not create CacheReader") + return nil + } + + return &writeCacheReader{ + originalReader: r, + buffer: bytes.NewBuffer(make([]byte, 0)), + rileResponse: &f, + cache: cache, + cacheKey: cacheKey, + } +} diff --git a/server/gitea/client.go b/server/gitea/client.go index 16cba84..c63ee21 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -1,142 +1,276 @@ package gitea import ( + "bytes" "errors" "fmt" + "io" + "mime" + "net/http" "net/url" + "path" + "strconv" "strings" "time" + "code.gitea.io/sdk/gitea" "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" - "github.com/valyala/fastjson" -) -const ( - giteaAPIRepos = "/api/v1/repos/" - giteaObjectTypeHeader = "X-Gitea-Object-Type" + "codeberg.org/codeberg/pages/server/cache" ) var ErrorNotFound = errors.New("not found") +const ( + // cache key prefixe + branchTimestampCacheKeyPrefix = "branchTime" + defaultBranchCacheKeyPrefix = "defaultBranch" + rawContentCacheKeyPrefix = "rawContent" + + // pages server + PagesCacheIndicatorHeader = "X-Pages-Cache" + symlinkReadLimit = 10000 + + // gitea + giteaObjectTypeHeader = "X-Gitea-Object-Type" + objTypeSymlink = "symlink" + + // std + ETagHeader = "ETag" + ContentTypeHeader = "Content-Type" + ContentLengthHeader = "Content-Length" +) + type Client struct { - giteaRoot string - giteaAPIToken string - fastClient *fasthttp.Client - infoTimeout time.Duration - contentTimeout time.Duration + sdkClient *gitea.Client + responseCache cache.SetGetKey followSymlinks bool supportLFS bool + + forbiddenMimeTypes map[string]bool + defaultMimeType string } -// TODO: once golang v1.19 is min requirement, we can switch to 'JoinPath()' of 'net/url' package -func joinURL(baseURL string, paths ...string) string { - p := make([]string, 0, len(paths)) - for i := range paths { - path := strings.TrimSpace(paths[i]) - path = strings.Trim(path, "/") - if len(path) != 0 { - p = append(p, path) - } - } - - return baseURL + "/" + strings.Join(p, "/") -} - -func NewClient(giteaRoot, giteaAPIToken string, followSymlinks, supportLFS bool) (*Client, error) { +func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, followSymlinks, supportLFS bool) (*Client, error) { rootURL, err := url.Parse(giteaRoot) + if err != nil { + return nil, err + } giteaRoot = strings.Trim(rootURL.String(), "/") + stdClient := http.Client{Timeout: 10 * time.Second} + + // TODO: pass down + var ( + forbiddenMimeTypes map[string]bool + defaultMimeType string + ) + + if forbiddenMimeTypes == nil { + forbiddenMimeTypes = make(map[string]bool) + } + if defaultMimeType == "" { + defaultMimeType = "application/octet-stream" + } + + sdk, err := gitea.NewClient(giteaRoot, gitea.SetHTTPClient(&stdClient), gitea.SetToken(giteaAPIToken)) return &Client{ - giteaRoot: giteaRoot, - giteaAPIToken: giteaAPIToken, - infoTimeout: 5 * time.Second, - contentTimeout: 10 * time.Second, - fastClient: getFastHTTPClient(), + sdkClient: sdk, + responseCache: respCache, followSymlinks: followSymlinks, supportLFS: supportLFS, + + forbiddenMimeTypes: forbiddenMimeTypes, + defaultMimeType: defaultMimeType, }, err } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - resp, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) + reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) if err != nil { return nil, err } - return resp.Body(), nil + defer reader.Close() + return io.ReadAll(reader) } -func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (*fasthttp.Response, error) { - var apiURL string - if client.supportLFS { - apiURL = joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "media", resource+"?ref="+url.QueryEscape(ref)) - } else { - apiURL = joinURL(client.giteaRoot, giteaAPIRepos, targetOwner, targetRepo, "raw", resource+"?ref="+url.QueryEscape(ref)) - } - resp, err := client.do(client.contentTimeout, apiURL) - if err != nil { - return nil, err - } +func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) { + cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) + log := log.With().Str("cache_key", cacheKey).Logger() - if err != nil { - return nil, err - } - - switch resp.StatusCode() { - case fasthttp.StatusOK: - objType := string(resp.Header.Peek(giteaObjectTypeHeader)) - log.Trace().Msgf("server raw content object: %s", objType) - if client.followSymlinks && objType == "symlink" { - // TODO: limit to 1000 chars if we switched to std - linkDest := strings.TrimSpace(string(resp.Body())) - log.Debug().Msgf("follow symlink from '%s' to '%s'", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + // handle if cache entry exist + if cache, ok := client.responseCache.Get(cacheKey); ok { + cache := cache.(FileResponse) + cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) + // TODO: check against some timestamp missmatch?!? + if cache.Exists { + if cache.IsSymlink { + linkDest := string(cache.Body) + log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + } else { + log.Debug().Msg("[cache] return bytes") + return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil + } + } else { + return nil, cachedHeader, cachedStatusCode, ErrorNotFound } - - return resp, nil - - case fasthttp.StatusNotFound: - return nil, ErrorNotFound - - default: - return nil, fmt.Errorf("unexpected status code '%d'", resp.StatusCode()) } + + // not in cache, open reader via gitea api + reader, resp, err := client.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS) + if resp != nil { + switch resp.StatusCode { + case http.StatusOK: + // first handle symlinks + { + objType := resp.Header.Get(giteaObjectTypeHeader) + log.Trace().Msgf("server raw content object %q", objType) + if client.followSymlinks && objType == objTypeSymlink { + defer reader.Close() + // read limited chars for symlink + linkDestBytes, err := io.ReadAll(io.LimitReader(reader, symlinkReadLimit)) + if err != nil { + return nil, nil, http.StatusInternalServerError, err + } + linkDest := strings.TrimSpace(string(linkDestBytes)) + + // we store symlink not content to reduce duplicates in cache + if err := client.responseCache.Set(cacheKey, FileResponse{ + Exists: true, + IsSymlink: true, + Body: []byte(linkDest), + ETag: resp.Header.Get(ETagHeader), + }, fileCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + + log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + } + } + + // now we are sure it's content so set the MIME type + mimeType := client.getMimeTypeByExtension(resource) + resp.Response.Header.Set(ContentTypeHeader, mimeType) + + if !shouldRespBeSavedToCache(resp.Response) { + return reader, resp.Response.Header, resp.StatusCode, err + } + + // now we write to cache and respond at the sime time + fileResp := FileResponse{ + Exists: true, + ETag: resp.Header.Get(ETagHeader), + MimeType: mimeType, + } + return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil + + case http.StatusNotFound: + if err := client.responseCache.Set(cacheKey, FileResponse{ + Exists: false, + ETag: resp.Header.Get(ETagHeader), + }, fileCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + + return nil, resp.Response.Header, http.StatusNotFound, ErrorNotFound + default: + return nil, resp.Response.Header, resp.StatusCode, fmt.Errorf("unexpected status code '%d'", resp.StatusCode) + } + } + return nil, nil, http.StatusInternalServerError, err } -func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (time.Time, error) { - url := joinURL(client.giteaRoot, giteaAPIRepos, repoOwner, repoName, "branches", branchName) - res, err := client.do(client.infoTimeout, url) +func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) { + cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName) + + if stamp, ok := client.responseCache.Get(cacheKey); ok && stamp != nil { + branchTimeStamp := stamp.(*BranchTimestamp) + if branchTimeStamp.notFound { + log.Trace().Msgf("[cache] use branch %q not found", branchName) + return &BranchTimestamp{}, ErrorNotFound + } + log.Trace().Msgf("[cache] use branch %q exist", branchName) + return branchTimeStamp, nil + } + + branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName) if err != nil { - return time.Time{}, err + if resp != nil && resp.StatusCode == http.StatusNotFound { + log.Trace().Msgf("[cache] set cache branch %q not found", branchName) + if err := client.responseCache.Set(cacheKey, &BranchTimestamp{Branch: branchName, notFound: true}, branchExistenceCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return &BranchTimestamp{}, ErrorNotFound + } + return &BranchTimestamp{}, err } - if res.StatusCode() != fasthttp.StatusOK { - return time.Time{}, fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + if resp.StatusCode != http.StatusOK { + return &BranchTimestamp{}, fmt.Errorf("unexpected status code '%d'", resp.StatusCode) } - return time.Parse(time.RFC3339, fastjson.GetString(res.Body(), "commit", "timestamp")) + + stamp := &BranchTimestamp{ + Branch: branch.Name, + Timestamp: branch.Commit.Timestamp, + } + + log.Trace().Msgf("set cache branch [%s] exist", branchName) + if err := client.responseCache.Set(cacheKey, stamp, branchExistenceCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return stamp, nil } func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) { - url := joinURL(client.giteaRoot, giteaAPIRepos, repoOwner, repoName) - res, err := client.do(client.infoTimeout, url) + cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName) + + if branch, ok := client.responseCache.Get(cacheKey); ok && branch != nil { + return branch.(string), nil + } + + repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName) if err != nil { return "", err } - if res.StatusCode() != fasthttp.StatusOK { - return "", fmt.Errorf("unexpected status code '%d'", res.StatusCode()) + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("unexpected status code '%d'", resp.StatusCode) } - return fastjson.GetString(res.Body(), "default_branch"), nil + + branch := repo.DefaultBranch + if err := client.responseCache.Set(cacheKey, branch, defaultBranchCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return branch, nil } -func (client *Client) do(timeout time.Duration, url string) (*fasthttp.Response, error) { - req := fasthttp.AcquireRequest() - - req.SetRequestURI(url) - req.Header.Set(fasthttp.HeaderAuthorization, "token "+client.giteaAPIToken) - res := fasthttp.AcquireResponse() - - err := client.fastClient.DoTimeout(req, res, timeout) - - return res, err +func (client *Client) getMimeTypeByExtension(resource string) string { + mimeType := mime.TypeByExtension(path.Ext(resource)) + mimeTypeSplit := strings.SplitN(mimeType, ";", 2) + if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { + mimeType = client.defaultMimeType + } + log.Trace().Msgf("probe mime of %q is %q", resource, mimeType) + return mimeType +} + +func shouldRespBeSavedToCache(resp *http.Response) bool { + if resp == nil { + return false + } + + contentLengthRaw := resp.Header.Get(ContentLengthHeader) + if contentLengthRaw == "" { + return false + } + + contentLeng, err := strconv.ParseInt(contentLengthRaw, 10, 64) + if err != nil { + log.Error().Err(err).Msg("could not parse content length") + } + + // if content to big or could not be determined we not cache it + return contentLeng > 0 && contentLeng < fileCacheSizeLimit } diff --git a/server/gitea/client_test.go b/server/gitea/client_test.go deleted file mode 100644 index 7dbad68..0000000 --- a/server/gitea/client_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package gitea - -import ( - "net/url" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestJoinURL(t *testing.T) { - baseURL := "" - assert.EqualValues(t, "/", joinURL(baseURL)) - assert.EqualValues(t, "/", joinURL(baseURL, "", "")) - - baseURL = "http://wwow.url.com" - assert.EqualValues(t, "http://wwow.url.com/a/b/c/d", joinURL(baseURL, "a", "b/c/", "d")) - - baseURL = "http://wow.url.com/subpath/2" - assert.EqualValues(t, "http://wow.url.com/subpath/2/content.pdf", joinURL(baseURL, "/content.pdf")) - assert.EqualValues(t, "http://wow.url.com/subpath/2/wonderful.jpg", joinURL(baseURL, "wonderful.jpg")) - assert.EqualValues(t, "http://wow.url.com/subpath/2/raw/wonderful.jpg?ref=main", joinURL(baseURL, "raw", "wonderful.jpg"+"?ref="+url.QueryEscape("main"))) - assert.EqualValues(t, "http://wow.url.com/subpath/2/raw/wonderful.jpg%3Fref=main", joinURL(baseURL, "raw", "wonderful.jpg%3Fref=main")) -} diff --git a/server/gitea/fasthttp.go b/server/gitea/fasthttp.go deleted file mode 100644 index 4ff0f4a..0000000 --- a/server/gitea/fasthttp.go +++ /dev/null @@ -1,15 +0,0 @@ -package gitea - -import ( - "time" - - "github.com/valyala/fasthttp" -) - -func getFastHTTPClient() *fasthttp.Client { - return &fasthttp.Client{ - MaxConnDuration: 60 * time.Second, - MaxConnWaitTimeout: 1000 * time.Millisecond, - MaxConnsPerHost: 128 * 16, // TODO: adjust bottlenecks for best performance with Gitea! - } -} diff --git a/server/handler.go b/server/handler.go index fb8b419..894cd25 100644 --- a/server/handler.go +++ b/server/handler.go @@ -1,15 +1,17 @@ package server import ( - "bytes" + "fmt" + "net/http" + "path" "strings" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/dns" "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" @@ -17,42 +19,48 @@ import ( "codeberg.org/codeberg/pages/server/version" ) +const ( + headerAccessControlAllowOrigin = "Access-Control-Allow-Origin" + headerAccessControlAllowMethods = "Access-Control-Allow-Methods" +) + // Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain []byte, +func Handler(mainDomainSuffix, rawDomain string, giteaClient *gitea.Client, giteaRoot, rawInfoPage string, - blacklistedPaths, allowedCorsDomains [][]byte, - dnsLookupCache, canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, -) func(ctx *fasthttp.RequestCtx) { - return func(ctx *fasthttp.RequestCtx) { - log := log.With().Strs("Handler", []string{string(ctx.Request.Host()), string(ctx.Request.Header.RequestURI())}).Logger() + blacklistedPaths, allowedCorsDomains []string, + dnsLookupCache, canonicalDomainCache cache.SetGetKey, +) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + log := log.With().Strs("Handler", []string{string(req.Host), req.RequestURI}).Logger() + ctx := context.New(w, req) - ctx.Response.Header.Set("Server", "CodebergPages/"+version.Version) + ctx.RespWriter.Header().Set("Server", "CodebergPages/"+version.Version) // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin - ctx.Response.Header.Set("Referrer-Policy", "strict-origin-when-cross-origin") + ctx.RespWriter.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") // Enable browser caching for up to 10 minutes - ctx.Response.Header.Set("Cache-Control", "public, max-age=600") + ctx.RespWriter.Header().Set("Cache-Control", "public, max-age=600") - trimmedHost := utils.TrimHostPort(ctx.Request.Host()) + trimmedHost := utils.TrimHostPort(req.Host) // Add HSTS for RawDomain and MainDomainSuffix - if hsts := GetHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { - ctx.Response.Header.Set("Strict-Transport-Security", hsts) + if hsts := getHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { + ctx.RespWriter.Header().Set("Strict-Transport-Security", hsts) } // Block all methods not required for static pages - if !ctx.IsGet() && !ctx.IsHead() && !ctx.IsOptions() { - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - ctx.Error("Method not allowed", fasthttp.StatusMethodNotAllowed) + if !ctx.IsMethod(http.MethodGet) && !ctx.IsMethod(http.MethodHead) && !ctx.IsMethod(http.MethodOptions) { + ctx.RespWriter.Header().Set("Allow", http.MethodGet+", "+http.MethodHead+", "+http.MethodOptions) // duplic 1 + ctx.String("Method not allowed", http.StatusMethodNotAllowed) return } // Block blacklisted paths (like ACME challenges) for _, blacklistedPath := range blacklistedPaths { - if bytes.HasPrefix(ctx.Path(), blacklistedPath) { - html.ReturnErrorPage(ctx, fasthttp.StatusForbidden) + if strings.HasPrefix(ctx.Path(), blacklistedPath) { + html.ReturnErrorPage(ctx, "requested blacklisted path", http.StatusForbidden) return } } @@ -60,18 +68,19 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Allow CORS for specified domains allowCors := false for _, allowedCorsDomain := range allowedCorsDomains { - if bytes.Equal(trimmedHost, allowedCorsDomain) { + if strings.EqualFold(trimmedHost, allowedCorsDomain) { allowCors = true break } } if allowCors { - ctx.Response.Header.Set("Access-Control-Allow-Origin", "*") - ctx.Response.Header.Set("Access-Control-Allow-Methods", "GET, HEAD") + ctx.RespWriter.Header().Set(headerAccessControlAllowOrigin, "*") + ctx.RespWriter.Header().Set(headerAccessControlAllowMethods, http.MethodGet+", "+http.MethodHead) } - ctx.Response.Header.Set("Allow", "GET, HEAD, OPTIONS") - if ctx.IsOptions() { - ctx.Response.Header.SetStatusCode(fasthttp.StatusNoContent) + + ctx.RespWriter.Header().Set("Allow", http.MethodGet+", "+http.MethodHead+", "+http.MethodOptions) // duplic 1 + if ctx.IsMethod(http.MethodOptions) { + ctx.RespWriter.WriteHeader(http.StatusNoContent) return } @@ -83,9 +92,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will // also disallow search indexing and add a Link header to the canonical URL. - tryBranch := func(log zerolog.Logger, repo, branch string, path []string, canonicalLink string) bool { + // TODO: move into external func to not alert vars indirectly + tryBranch := func(log zerolog.Logger, repo, branch string, _path []string, canonicalLink string) bool { if repo == "" { - log.Warn().Msg("tryBranch: repo is empty") + log.Debug().Msg("tryBranch: repo is empty") return false } @@ -94,23 +104,23 @@ func Handler(mainDomainSuffix, rawDomain []byte, branch = strings.ReplaceAll(branch, "~", "/") // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch, branchTimestampCache) + branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch) if branchTimestampResult == nil { - log.Warn().Msg("tryBranch: branch doesn't exist") + log.Debug().Msg("tryBranch: branch doesn't exist") return false } // Branch exists, use it targetRepo = repo - targetPath = strings.Trim(strings.Join(path, "/"), "/") + targetPath = path.Join(_path...) targetBranch = branchTimestampResult.Branch targetOptions.BranchTimestamp = branchTimestampResult.Timestamp if canonicalLink != "" { // Hide from search machines & add canonical link - ctx.Response.Header.Set("X-Robots-Tag", "noarchive, noindex") - ctx.Response.Header.Set("Link", + ctx.RespWriter.Header().Set("X-Robots-Tag", "noarchive, noindex") + ctx.RespWriter.Header().Set("Link", strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ "; rel=\"canonical\"", ) @@ -120,22 +130,18 @@ func Handler(mainDomainSuffix, rawDomain []byte, return true } - log.Debug().Msg("Preparing") - if rawDomain != nil && bytes.Equal(trimmedHost, rawDomain) { + log.Debug().Msg("preparations") + if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { // Serve raw content from RawDomain - log.Debug().Msg("Serving raw domain") + log.Debug().Msg("raw domain") targetOptions.TryIndexPages = false - if targetOptions.ForbiddenMimeTypes == nil { - targetOptions.ForbiddenMimeTypes = make(map[string]bool) - } - targetOptions.ForbiddenMimeTypes["text/html"] = true - targetOptions.DefaultMimeType = "text/plain; charset=utf-8" + targetOptions.ServeRaw = true - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") if len(pathElements) < 2 { // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required - ctx.Redirect(rawInfoPage, fasthttp.StatusTemporaryRedirect) + ctx.Redirect(rawInfoPage, http.StatusTemporaryRedirect) return } targetOwner = pathElements[0] @@ -143,45 +149,45 @@ func Handler(mainDomainSuffix, rawDomain []byte, // raw.codeberg.org/example/myrepo/@main/index.html if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { - log.Debug().Msg("Preparing raw domain, now trying with specified branch") + log.Debug().Msg("raw domain preparations, now trying with specified branch") if tryBranch(log, targetRepo, pathElements[2][1:], pathElements[3:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) { - log.Info().Msg("tryBranch, now trying upstream 1") + log.Debug().Msg("tryBranch, now trying upstream 1") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) return } - log.Warn().Msg("Path missed a branch") - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + log.Debug().Msg("missing branch info") + html.ReturnErrorPage(ctx, "missing branch info", http.StatusFailedDependency) return } - log.Debug().Msg("Preparing raw domain, now trying with default branch") + log.Debug().Msg("raw domain preparations, now trying with default branch") tryBranch(log, targetRepo, "", pathElements[2:], giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", ) - log.Info().Msg("tryBranch, now trying upstream 2") + log.Debug().Msg("tryBranch, now trying upstream 2") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) return - } else if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { + } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { // Serve pages from subdomains of MainDomainSuffix - log.Info().Msg("Serve pages from main domain suffix") + log.Debug().Msg("main domain suffix") - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") - targetOwner = string(bytes.TrimSuffix(trimmedHost, mainDomainSuffix)) + pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") + targetOwner = strings.TrimSuffix(trimmedHost, mainDomainSuffix) targetRepo = pathElements[0] targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") if targetOwner == "www" { // www.codeberg.page redirects to codeberg.page // TODO: rm hardcoded - use cname? - ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), fasthttp.StatusPermanentRedirect) + ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), http.StatusPermanentRedirect) return } @@ -190,22 +196,24 @@ func Handler(mainDomainSuffix, rawDomain []byte, if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { if targetRepo == "pages" { // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... - ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), fasthttp.StatusTemporaryRedirect) + ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), http.StatusTemporaryRedirect) return } - log.Debug().Msg("Preparing main domain, now trying with specified repo & branch") + log.Debug().Msg("main domain preparations, now trying with specified repo & branch") + branch := pathElements[1][1:] if tryBranch(log, - pathElements[0], pathElements[1][1:], pathElements[2:], + pathElements[0], branch, pathElements[2:], "/"+pathElements[0]+"/%p", ) { - log.Info().Msg("tryBranch, now trying upstream 3") + log.Debug().Msg("tryBranch, now trying upstream 3") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) } else { - log.Warn().Msg("tryBranch: upstream 3 failed") - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, + fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", branch, targetOwner, targetRepo), + http.StatusFailedDependency) } return } @@ -213,16 +221,18 @@ func Handler(mainDomainSuffix, rawDomain []byte, // Check if the first directory is a branch for the "pages" repo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { - log.Debug().Msg("Preparing main domain, now trying with specified branch") + log.Debug().Msg("main domain preparations, now trying with specified branch") + branch := pathElements[0][1:] if tryBranch(log, - "pages", pathElements[0][1:], pathElements[1:], "/%p") { - log.Info().Msg("tryBranch, now trying upstream 4") + "pages", branch, pathElements[1:], "/%p") { + log.Debug().Msg("tryBranch, now trying upstream 4") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + targetOptions, targetOwner, "pages", targetBranch, targetPath, + canonicalDomainCache) } else { - log.Warn().Msg("tryBranch: upstream 4 failed") - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, + fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", branch, targetOwner, "pages"), + http.StatusFailedDependency) } return } @@ -233,10 +243,10 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with specified repo") if pathElements[0] != "pages" && tryBranch(log, pathElements[0], "pages", pathElements[1:], "") { - log.Info().Msg("tryBranch, now trying upstream 5") + log.Debug().Msg("tryBranch, now trying upstream 5") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) return } @@ -245,28 +255,31 @@ func Handler(mainDomainSuffix, rawDomain []byte, log.Debug().Msg("main domain preparations, now trying with default repo/branch") if tryBranch(log, "pages", "", pathElements, "") { - log.Info().Msg("tryBranch, now trying upstream 6") + log.Debug().Msg("tryBranch, now trying upstream 6") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) return } // Couldn't find a valid repo/branch - - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, + fmt.Sprintf("couldn't find a valid repo[%s]/branch[%s]", targetRepo, targetBranch), + http.StatusFailedDependency) return } else { trimmedHostStr := string(trimmedHost) - // Serve pages from external domains + // Serve pages from custom domains targetOwner, targetRepo, targetBranch = dns.GetTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) if targetOwner == "" { - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, + "could not obtain repo owner from custom domain", + http.StatusFailedDependency) return } - pathElements := strings.Split(string(bytes.Trim(ctx.Request.URI().Path(), "/")), "/") + pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") canonicalLink := "" if strings.HasPrefix(pathElements[0], "@") { targetBranch = pathElements[0][1:] @@ -275,36 +288,33 @@ func Handler(mainDomainSuffix, rawDomain []byte, } // Try to use the given repo on the given branch or the default branch - log.Debug().Msg("Preparing custom domain, now trying with details from DNS") + log.Debug().Msg("custom domain preparations, now trying with details from DNS") if tryBranch(log, targetRepo, targetBranch, pathElements, canonicalLink) { canonicalDomain, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), canonicalDomainCache) if !valid { - log.Warn().Msg("Custom domains, domain from DNS isn't valid/canonical") - html.ReturnErrorPage(ctx, fasthttp.StatusMisdirectedRequest) + html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHostStr { // only redirect if the target is also a codeberg page! targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) if targetOwner != "" { - ctx.Redirect("https://"+canonicalDomain+string(ctx.RequestURI()), fasthttp.StatusTemporaryRedirect) + ctx.Redirect("https://"+canonicalDomain+string(ctx.Path()), http.StatusTemporaryRedirect) return } - log.Warn().Msg("Custom domains, targetOwner from DNS is empty") - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, "target is no codeberg page", http.StatusFailedDependency) return } - log.Info().Msg("tryBranch, now trying upstream 7") + log.Debug().Msg("tryBranch, now trying upstream 7") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache, branchTimestampCache, fileResponseCache) + canonicalDomainCache) return } - log.Warn().Msg("Couldn't handle request, none of the options succeed") - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + html.ReturnErrorPage(ctx, "could not find target for custom domain", http.StatusFailedDependency) return } } diff --git a/server/handler_test.go b/server/handler_test.go index f9a721a..c0aca14 100644 --- a/server/handler_test.go +++ b/server/handler_test.go @@ -1,44 +1,42 @@ package server import ( - "fmt" + "net/http/httptest" "testing" "time" - "github.com/valyala/fasthttp" - "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/gitea" + "github.com/rs/zerolog/log" ) func TestHandlerPerformance(t *testing.T) { giteaRoot := "https://codeberg.org" - giteaClient, _ := gitea.NewClient(giteaRoot, "", false, false) + giteaClient, _ := gitea.NewClient(giteaRoot, "", cache.NewKeyValueCache(), false, false) testHandler := Handler( - []byte("codeberg.page"), []byte("raw.codeberg.org"), + "codeberg.page", "raw.codeberg.org", giteaClient, giteaRoot, "https://docs.codeberg.org/pages/raw-content/", - [][]byte{[]byte("/.well-known/acme-challenge/")}, - [][]byte{[]byte("raw.codeberg.org"), []byte("fonts.codeberg.org"), []byte("design.codeberg.org")}, - cache.NewKeyValueCache(), - cache.NewKeyValueCache(), + []string{"/.well-known/acme-challenge/"}, + []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, cache.NewKeyValueCache(), cache.NewKeyValueCache(), ) testCase := func(uri string, status int) { - ctx := &fasthttp.RequestCtx{ - Request: *fasthttp.AcquireRequest(), - Response: *fasthttp.AcquireResponse(), - } - ctx.Request.SetRequestURI(uri) - fmt.Printf("Start: %v\n", time.Now()) + req := httptest.NewRequest("GET", uri, nil) + w := httptest.NewRecorder() + + log.Printf("Start: %v\n", time.Now()) start := time.Now() - testHandler(ctx) + testHandler(w, req) end := time.Now() - fmt.Printf("Done: %v\n", time.Now()) - if ctx.Response.StatusCode() != status { - t.Errorf("request failed with status code %d", ctx.Response.StatusCode()) + log.Printf("Done: %v\n", time.Now()) + + resp := w.Result() + + if resp.StatusCode != status { + t.Errorf("request failed with status code %d", resp.StatusCode) } else { t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) } diff --git a/server/helpers.go b/server/helpers.go index 6d55ddf..7c898cd 100644 --- a/server/helpers.go +++ b/server/helpers.go @@ -1,13 +1,13 @@ package server import ( - "bytes" + "strings" ) -// GetHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty +// getHSTSHeader returns a HSTS header with includeSubdomains & preload for MainDomainSuffix and RawDomain, or an empty // string for custom domains. -func GetHSTSHeader(host, mainDomainSuffix, rawDomain []byte) string { - if bytes.HasSuffix(host, mainDomainSuffix) || bytes.Equal(host, rawDomain) { +func getHSTSHeader(host, mainDomainSuffix, rawDomain string) string { + if strings.HasSuffix(host, mainDomainSuffix) || strings.EqualFold(host, rawDomain) { return "max-age=63072000; includeSubdomains; preload" } else { return "" diff --git a/server/setup.go b/server/setup.go index 176bb42..e7194ed 100644 --- a/server/setup.go +++ b/server/setup.go @@ -1,53 +1,27 @@ package server import ( - "bytes" - "fmt" "net/http" - "time" - - "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" + "strings" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/utils" ) -type fasthttpLogger struct{} +func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc { + challengePath := "/.well-known/acme-challenge/" -func (fasthttpLogger) Printf(format string, args ...interface{}) { - log.Printf("FastHTTP: %s", fmt.Sprintf(format, args...)) -} - -func SetupServer(handler fasthttp.RequestHandler) *fasthttp.Server { - // Enable compression by wrapping the handler with the compression function provided by FastHTTP - compressedHandler := fasthttp.CompressHandlerBrotliLevel(handler, fasthttp.CompressBrotliBestSpeed, fasthttp.CompressBestSpeed) - - return &fasthttp.Server{ - Handler: compressedHandler, - DisablePreParseMultipartForm: true, - NoDefaultServerHeader: true, - NoDefaultDate: true, - ReadTimeout: 30 * time.Second, // needs to be this high for ACME certificates with ZeroSSL & HTTP-01 challenge - Logger: fasthttpLogger{}, - } -} - -func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) *fasthttp.Server { - challengePath := []byte("/.well-known/acme-challenge/") - - return &fasthttp.Server{ - Handler: func(ctx *fasthttp.RequestCtx) { - if bytes.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(string(utils.TrimHostPort(ctx.Host())) + "/" + string(bytes.TrimPrefix(ctx.Path(), challengePath))) - if !ok || challenge == nil { - ctx.SetStatusCode(http.StatusNotFound) - ctx.SetBodyString("no challenge for this token") - } - ctx.SetBodyString(challenge.(string)) - } else { - ctx.Redirect("https://"+string(ctx.Host())+string(ctx.RequestURI()), http.StatusMovedPermanently) + return func(w http.ResponseWriter, req *http.Request) { + ctx := context.New(w, req) + if strings.HasPrefix(ctx.Path(), challengePath) { + challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + string(strings.TrimPrefix(ctx.Path(), challengePath))) + if !ok || challenge == nil { + ctx.String("no challenge for this token", http.StatusNotFound) } - }, + ctx.String(challenge.(string)) + } else { + ctx.Redirect("https://"+string(ctx.Host())+string(ctx.Path()), http.StatusMovedPermanently) + } } } diff --git a/server/try.go b/server/try.go index 24831c4..135c1e0 100644 --- a/server/try.go +++ b/server/try.go @@ -1,38 +1,37 @@ package server import ( - "bytes" + "net/http" "strings" - "github.com/valyala/fasthttp" - "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/upstream" ) // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. -func tryUpstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, - mainDomainSuffix, trimmedHost []byte, +func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, + mainDomainSuffix, trimmedHost string, targetOptions *upstream.Options, targetOwner, targetRepo, targetBranch, targetPath string, - canonicalDomainCache, branchTimestampCache, fileResponseCache cache.SetGetKey, + canonicalDomainCache cache.SetGetKey, ) { // check if a canonical domain exists on a request on MainDomain - if bytes.HasSuffix(trimmedHost, mainDomainSuffix) { + if strings.HasSuffix(trimmedHost, mainDomainSuffix) { canonicalDomain, _ := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { - canonicalPath := string(ctx.RequestURI()) + canonicalPath := ctx.Req.RequestURI if targetRepo != "pages" { path := strings.SplitN(canonicalPath, "/", 3) if len(path) >= 3 { canonicalPath = "/" + path[2] } } - ctx.Redirect("https://"+canonicalDomain+canonicalPath, fasthttp.StatusTemporaryRedirect) + ctx.Redirect("https://"+canonicalDomain+canonicalPath, http.StatusTemporaryRedirect) return } } @@ -44,7 +43,7 @@ func tryUpstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, targetOptions.Host = string(trimmedHost) // Try to request the file from the Gitea API - if !targetOptions.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { - html.ReturnErrorPage(ctx, ctx.Response.StatusCode()) + if !targetOptions.Upstream(ctx, giteaClient) { + html.ReturnErrorPage(ctx, "", ctx.StatusCode) } } diff --git a/server/upstream/const.go b/server/upstream/const.go deleted file mode 100644 index 247e1d1..0000000 --- a/server/upstream/const.go +++ /dev/null @@ -1,24 +0,0 @@ -package upstream - -import "time" - -// defaultBranchCacheTimeout specifies the timeout for the default branch cache. It can be quite long. -var defaultBranchCacheTimeout = 15 * time.Minute - -// branchExistenceCacheTimeout specifies the timeout for the branch timestamp & existence cache. It should be shorter -// than fileCacheTimeout, as that gets invalidated if the branch timestamp has changed. That way, repo changes will be -// picked up faster, while still allowing the content to be cached longer if nothing changes. -var branchExistenceCacheTimeout = 5 * time.Minute - -// fileCacheTimeout specifies the timeout for the file content cache - you might want to make this quite long, depending -// on your available memory. -// TODO: move as option into cache interface -var fileCacheTimeout = 5 * time.Minute - -// fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. -var fileCacheSizeLimit = 1024 * 1024 - -// canonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. -var canonicalDomainCacheTimeout = 15 * time.Minute - -const canonicalDomainConfig = ".domains" diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 553c148..6ad6506 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -2,11 +2,19 @@ package upstream import ( "strings" + "time" + + "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/gitea" ) +// canonicalDomainCacheTimeout specifies the timeout for the canonical domain cache. +var canonicalDomainCacheTimeout = 15 * time.Minute + +const canonicalDomainConfig = ".domains" + // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (string, bool) { var ( @@ -36,6 +44,8 @@ func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, ta valid = true } } + } else { + log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, targetOwner, targetRepo) } domains = append(domains, targetOwner+mainDomainSuffix) if domains[len(domains)-1] == actualDomain { diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 28f4474..6bc23c8 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -1,84 +1,36 @@ package upstream import ( - "mime" - "path" - "strconv" - "strings" - "time" + "errors" - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/gitea" "github.com/rs/zerolog/log" -) -type branchTimestamp struct { - Branch string - Timestamp time.Time -} + "codeberg.org/codeberg/pages/server/gitea" +) // GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch // (or nil if the branch doesn't exist) -func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string, branchTimestampCache cache.SetGetKey) *branchTimestamp { +func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string) *gitea.BranchTimestamp { log := log.With().Strs("BranchInfo", []string{owner, repo, branch}).Logger() - if result, ok := branchTimestampCache.Get(owner + "/" + repo + "/" + branch); ok { - if result == nil { - log.Debug().Msg("branchTimestampCache found item, but result is empty") - return nil - } - log.Debug().Msg("branchTimestampCache found item, returning result") - return result.(*branchTimestamp) - } - result := &branchTimestamp{ - Branch: branch, - } + if len(branch) == 0 { // Get default branch defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(owner, repo) if err != nil { log.Err(err).Msg("Could't fetch default branch from repository") - _ = branchTimestampCache.Set(owner+"/"+repo+"/", nil, defaultBranchCacheTimeout) return nil } - log.Debug().Msg("Succesfully fetched default branch from Gitea") - result.Branch = defaultBranch + log.Debug().Msgf("Succesfully fetched default branch %q from Gitea", defaultBranch) + branch = defaultBranch } - timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(owner, repo, result.Branch) + timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(owner, repo, branch) if err != nil { - log.Err(err).Msg("Could not get latest commit's timestamp from branch") + if !errors.Is(err, gitea.ErrorNotFound) { + log.Error().Err(err).Msg("Could not get latest commit's timestamp from branch") + } return nil } - log.Debug().Msg("Succesfully fetched latest commit's timestamp from branch, adding to cache") - result.Timestamp = timestamp - _ = branchTimestampCache.Set(owner+"/"+repo+"/"+branch, result, branchExistenceCacheTimeout) - return result -} - -func (o *Options) getMimeTypeByExtension() string { - if o.ForbiddenMimeTypes == nil { - o.ForbiddenMimeTypes = make(map[string]bool) - } - mimeType := mime.TypeByExtension(path.Ext(o.TargetPath)) - mimeTypeSplit := strings.SplitN(mimeType, ";", 2) - if o.ForbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { - if o.DefaultMimeType != "" { - mimeType = o.DefaultMimeType - } else { - mimeType = "application/octet-stream" - } - } - return mimeType -} - -func (o *Options) generateUri() string { - return path.Join(o.TargetOwner, o.TargetRepo, "raw", o.TargetBranch, o.TargetPath) -} - -func (o *Options) generateUriClientArgs() (targetOwner, targetRepo, ref, resource string) { - return o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath -} - -func (o *Options) timestamp() string { - return strconv.FormatInt(o.BranchTimestamp.Unix(), 10) + log.Debug().Msgf("Succesfully fetched latest commit's timestamp from branch: %#v", timestamp) + return timestamp } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 61c90de..d37c35e 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -1,20 +1,27 @@ package upstream import ( - "bytes" "errors" + "fmt" "io" + "net/http" "strings" "time" "github.com/rs/zerolog/log" - "github.com/valyala/fasthttp" "codeberg.org/codeberg/pages/html" - "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/gitea" ) +const ( + headerLastModified = "Last-Modified" + headerIfModifiedSince = "If-Modified-Since" + + rawMime = "text/plain; charset=utf-8" +) + // upstreamIndexPages lists pages that may be considered as index pages for directories. var upstreamIndexPages = []string{ "index.html", @@ -35,61 +42,61 @@ type Options struct { // Used for debugging purposes. Host string - DefaultMimeType string - ForbiddenMimeTypes map[string]bool - TryIndexPages bool - BranchTimestamp time.Time + TryIndexPages bool + BranchTimestamp time.Time // internal appendTrailingSlash bool redirectIfExists string + + ServeRaw bool } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, branchTimestampCache, fileResponseCache cache.SetGetKey) (final bool) { - log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath, o.Host}).Logger() +func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (final bool) { + log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() + + if o.TargetOwner == "" || o.TargetRepo == "" { + html.ReturnErrorPage(ctx, "either repo owner or name info is missing", http.StatusBadRequest) + return true + } // Check if the branch exists and when it was modified if o.BranchTimestamp.IsZero() { - branch := GetBranchTimestamp(giteaClient, o.TargetOwner, o.TargetRepo, o.TargetBranch, branchTimestampCache) + branch := GetBranchTimestamp(giteaClient, o.TargetOwner, o.TargetRepo, o.TargetBranch) - if branch == nil { - html.ReturnErrorPage(ctx, fasthttp.StatusFailedDependency) + if branch == nil || branch.Branch == "" { + html.ReturnErrorPage(ctx, + fmt.Sprintf("could not get timestamp of branch %q", o.TargetBranch), + http.StatusFailedDependency) return true } o.TargetBranch = branch.Branch o.BranchTimestamp = branch.Timestamp } - if o.TargetOwner == "" || o.TargetRepo == "" || o.TargetBranch == "" { - html.ReturnErrorPage(ctx, fasthttp.StatusBadRequest) - return true - } - // Check if the browser has a cached version - if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Request.Header.Peek("If-Modified-Since"))); err == nil { - if !ifModifiedSince.Before(o.BranchTimestamp) { - ctx.Response.SetStatusCode(fasthttp.StatusNotModified) - return true + if ctx.Response() != nil { + if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Response().Header.Get(headerIfModifiedSince))); err == nil { + if !ifModifiedSince.Before(o.BranchTimestamp) { + ctx.RespWriter.WriteHeader(http.StatusNotModified) + log.Trace().Msg("check response against last modified: valid") + return true + } } + log.Trace().Msg("check response against last modified: outdated") } log.Debug().Msg("Preparing") - // Make a GET request to the upstream URL - uri := o.generateUri() - var res *fasthttp.Response - var cachedResponse gitea.FileResponse - var err error - if cachedValue, ok := fileResponseCache.Get(uri + "?timestamp=" + o.timestamp()); ok && !cachedValue.(gitea.FileResponse).IsEmpty() { - cachedResponse = cachedValue.(gitea.FileResponse) - } else { - res, err = giteaClient.ServeRawContent(o.generateUriClientArgs()) + reader, header, statusCode, err := giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath) + if reader != nil { + defer reader.Close() } log.Debug().Msg("Aquisting") - // Handle errors - if (err != nil && errors.Is(err, gitea.ErrorNotFound)) || (res == nil && !cachedResponse.Exists) { + // Handle not found error + if err != nil && errors.Is(err, gitea.ErrorNotFound) { if o.TryIndexPages { // copy the o struct & try if an index page exists optionsForIndexPages := *o @@ -97,25 +104,20 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, optionsForIndexPages.appendTrailingSlash = true for _, indexPage := range upstreamIndexPages { optionsForIndexPages.TargetPath = strings.TrimSuffix(o.TargetPath, "/") + "/" + indexPage - if optionsForIndexPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ - Exists: false, - }, fileCacheTimeout) + if optionsForIndexPages.Upstream(ctx, giteaClient) { return true } } // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.appendTrailingSlash = false - optionsForIndexPages.redirectIfExists = strings.TrimSuffix(string(ctx.Request.URI().Path()), "/") + ".html" + optionsForIndexPages.redirectIfExists = strings.TrimSuffix(ctx.Path(), "/") + ".html" optionsForIndexPages.TargetPath = o.TargetPath + ".html" - if optionsForIndexPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ - Exists: false, - }, fileCacheTimeout) + if optionsForIndexPages.Upstream(ctx, giteaClient) { return true } } - ctx.Response.SetStatusCode(fasthttp.StatusNotFound) + + ctx.StatusCode = http.StatusNotFound if o.TryIndexPages { // copy the o struct & try if a not found page exists optionsForNotFoundPages := *o @@ -123,94 +125,84 @@ func (o *Options) Upstream(ctx *fasthttp.RequestCtx, giteaClient *gitea.Client, optionsForNotFoundPages.appendTrailingSlash = false for _, notFoundPage := range upstreamNotFoundPages { optionsForNotFoundPages.TargetPath = "/" + notFoundPage - if optionsForNotFoundPages.Upstream(ctx, giteaClient, branchTimestampCache, fileResponseCache) { - _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ - Exists: false, - }, fileCacheTimeout) + if optionsForNotFoundPages.Upstream(ctx, giteaClient) { return true } } } - if res != nil { - // Update cache if the request is fresh - _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), gitea.FileResponse{ - Exists: false, - }, fileCacheTimeout) - } return false } - if res != nil && (err != nil || res.StatusCode() != fasthttp.StatusOK) { - log.Warn().Msgf("Couldn't fetch contents from %q: %v (status code %d)", uri, err, res.StatusCode()) - html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) + + // handle unexpected client errors + if err != nil || reader == nil || statusCode != http.StatusOK { + log.Debug().Msg("Handling error") + var msg string + + if err != nil { + msg = "gitea client returned unexpected error" + log.Error().Err(err).Msg(msg) + msg = fmt.Sprintf("%s: %v", msg, err) + } + if reader == nil { + msg = "gitea client returned no reader" + log.Error().Msg(msg) + } + if statusCode != http.StatusOK { + msg = fmt.Sprintf("Couldn't fetch contents (status code %d)", statusCode) + log.Error().Msg(msg) + } + + html.ReturnErrorPage(ctx, msg, http.StatusInternalServerError) return true } // Append trailing slash if missing (for index files), and redirect to fix filenames in general // o.appendTrailingSlash is only true when looking for index pages - if o.appendTrailingSlash && !bytes.HasSuffix(ctx.Request.URI().Path(), []byte{'/'}) { - ctx.Redirect(string(ctx.Request.URI().Path())+"/", fasthttp.StatusTemporaryRedirect) + if o.appendTrailingSlash && !strings.HasSuffix(ctx.Path(), "/") { + ctx.Redirect(ctx.Path()+"/", http.StatusTemporaryRedirect) return true } - if bytes.HasSuffix(ctx.Request.URI().Path(), []byte("/index.html")) { - ctx.Redirect(strings.TrimSuffix(string(ctx.Request.URI().Path()), "index.html"), fasthttp.StatusTemporaryRedirect) + if strings.HasSuffix(ctx.Path(), "/index.html") { + ctx.Redirect(strings.TrimSuffix(ctx.Path(), "index.html"), http.StatusTemporaryRedirect) return true } if o.redirectIfExists != "" { - ctx.Redirect(o.redirectIfExists, fasthttp.StatusTemporaryRedirect) + ctx.Redirect(o.redirectIfExists, http.StatusTemporaryRedirect) return true } - log.Debug().Msg("Handling error") - - // Set the MIME type - mimeType := o.getMimeTypeByExtension() - ctx.Response.Header.SetContentType(mimeType) - - // Set ETag - if cachedResponse.Exists { - ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag) - } else if res != nil { - cachedResponse.ETag = res.Header.Peek(fasthttp.HeaderETag) - ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, cachedResponse.ETag) + // Set ETag & MIME + if eTag := header.Get(gitea.ETagHeader); eTag != "" { + ctx.RespWriter.Header().Set(gitea.ETagHeader, eTag) } - - if ctx.Response.StatusCode() != fasthttp.StatusNotFound { - // Everything's okay so far - ctx.Response.SetStatusCode(fasthttp.StatusOK) + if cacheIndicator := header.Get(gitea.PagesCacheIndicatorHeader); cacheIndicator != "" { + ctx.RespWriter.Header().Set(gitea.PagesCacheIndicatorHeader, cacheIndicator) } - ctx.Response.Header.SetLastModified(o.BranchTimestamp) + if length := header.Get(gitea.ContentLengthHeader); length != "" { + ctx.RespWriter.Header().Set(gitea.ContentLengthHeader, length) + } + if mime := header.Get(gitea.ContentTypeHeader); mime == "" || o.ServeRaw { + ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, rawMime) + } else { + ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) + } + ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(time.RFC1123)) log.Debug().Msg("Prepare response") - // Write the response body to the original request - var cacheBodyWriter bytes.Buffer - if res != nil { - if res.Header.ContentLength() > fileCacheSizeLimit { - // fasthttp else will set "Content-Length: 0" - ctx.Response.SetBodyStream(&strings.Reader{}, -1) + ctx.RespWriter.WriteHeader(ctx.StatusCode) - err = res.BodyWriteTo(ctx.Response.BodyWriter()) - } else { - // TODO: cache is half-empty if request is cancelled - does the ctx.Err() below do the trick? - err = res.BodyWriteTo(io.MultiWriter(ctx.Response.BodyWriter(), &cacheBodyWriter)) + // Write the response body to the original request + if reader != nil { + _, err := io.Copy(ctx.RespWriter, reader) + if err != nil { + log.Error().Err(err).Msgf("Couldn't write body for %q", o.TargetPath) + html.ReturnErrorPage(ctx, "", http.StatusInternalServerError) + return true } - } else { - _, err = ctx.Write(cachedResponse.Body) - } - if err != nil { - log.Error().Err(err).Msgf("Couldn't write body for %q", uri) - html.ReturnErrorPage(ctx, fasthttp.StatusInternalServerError) - return true } log.Debug().Msg("Sending response") - if res != nil && res.Header.ContentLength() <= fileCacheSizeLimit && ctx.Err() == nil { - cachedResponse.Exists = true - cachedResponse.MimeType = mimeType - cachedResponse.Body = cacheBodyWriter.Bytes() - _ = fileResponseCache.Set(uri+"?timestamp="+o.timestamp(), cachedResponse, fileCacheTimeout) - } - return true } diff --git a/server/utils/utils.go b/server/utils/utils.go index 7be330f..30f948d 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -1,9 +1,11 @@ package utils -import "bytes" +import ( + "strings" +) -func TrimHostPort(host []byte) []byte { - i := bytes.IndexByte(host, ':') +func TrimHostPort(host string) string { + i := strings.IndexByte(host, ':') if i >= 0 { return host[:i] } diff --git a/server/utils/utils_test.go b/server/utils/utils_test.go index 3dc0632..2532392 100644 --- a/server/utils/utils_test.go +++ b/server/utils/utils_test.go @@ -7,7 +7,7 @@ import ( ) func TestTrimHostPort(t *testing.T) { - assert.EqualValues(t, "aa", TrimHostPort([]byte("aa"))) - assert.EqualValues(t, "", TrimHostPort([]byte(":"))) - assert.EqualValues(t, "example.com", TrimHostPort([]byte("example.com:80"))) + assert.EqualValues(t, "aa", TrimHostPort("aa")) + assert.EqualValues(t, "", TrimHostPort(":")) + assert.EqualValues(t, "example.com", TrimHostPort("example.com:80")) } From 6c63b66ce44535cb2fdc3543780e01fa9994e3d5 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 20:43:44 +0100 Subject: [PATCH 167/320] Refactor split long functions (#135) we have big functions that handle all stuff ... we should split this into smaler chuncks so we could test them seperate and make clear cuts in what happens where Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/135 --- cmd/main.go | 5 +- server/certificates/certificates.go | 8 +- server/context/context.go | 10 +- server/gitea/client.go | 8 + server/handler.go | 321 ------------------------ server/handler/handler.go | 111 ++++++++ server/handler/handler_custom_domain.go | 71 ++++++ server/handler/handler_raw_domain.go | 67 +++++ server/handler/handler_sub_domain.go | 120 +++++++++ server/{ => handler}/handler_test.go | 7 +- server/{helpers.go => handler/hsts.go} | 2 +- server/handler/try.go | 76 ++++++ server/try.go | 49 ---- server/upstream/domains.go | 16 +- server/upstream/header.go | 28 +++ server/upstream/helper.go | 33 ++- server/upstream/upstream.go | 42 ++-- 17 files changed, 547 insertions(+), 427 deletions(-) delete mode 100644 server/handler.go create mode 100644 server/handler/handler.go create mode 100644 server/handler/handler_custom_domain.go create mode 100644 server/handler/handler_raw_domain.go create mode 100644 server/handler/handler_sub_domain.go rename server/{ => handler}/handler_test.go (85%) rename server/{helpers.go => handler/hsts.go} (96%) create mode 100644 server/handler/try.go delete mode 100644 server/try.go create mode 100644 server/upstream/header.go diff --git a/cmd/main.go b/cmd/main.go index a3a61e1..6ad1aa8 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -20,6 +20,7 @@ import ( "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/handler" ) // AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. @@ -88,9 +89,9 @@ func Serve(ctx *cli.Context) error { } // Create handler based on settings - httpsHandler := server.Handler(mainDomainSuffix, rawDomain, + httpsHandler := handler.Handler(mainDomainSuffix, rawDomain, giteaClient, - giteaRoot, rawInfoPage, + rawInfoPage, BlacklistedPaths, allowedCorsDomains, dnsLookupCache, canonicalDomainCache) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 429ab23..42620b1 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -78,8 +78,12 @@ func TLSConfig(mainDomainSuffix string, // DNS not set up, return main certificate to redirect to the docs sni = mainDomainSuffix } else { - _, _ = targetRepo, targetBranch - _, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, sni, mainDomainSuffix, canonicalDomainCache) + targetOpt := &upstream.Options{ + TargetOwner: targetOwner, + TargetRepo: targetRepo, + TargetBranch: targetBranch, + } + _, valid := targetOpt.CheckCanonicalDomain(giteaClient, sni, mainDomainSuffix, canonicalDomainCache) if !valid { sni = mainDomainSuffix } diff --git a/server/context/context.go b/server/context/context.go index be01df0..481fee2 100644 --- a/server/context/context.go +++ b/server/context/context.go @@ -3,6 +3,8 @@ package context import ( stdContext "context" "net/http" + + "codeberg.org/codeberg/pages/server/utils" ) type Context struct { @@ -42,10 +44,6 @@ func (c *Context) String(raw string, status ...int) { _, _ = c.RespWriter.Write([]byte(raw)) } -func (c *Context) IsMethod(m string) bool { - return c.Req.Method == m -} - func (c *Context) Redirect(uri string, statusCode int) { http.Redirect(c.RespWriter, c.Req, uri, statusCode) } @@ -60,3 +58,7 @@ func (c *Context) Path() string { func (c *Context) Host() string { return c.Req.URL.Host } + +func (c *Context) TrimHostPort() string { + return utils.TrimHostPort(c.Req.Host) +} diff --git a/server/gitea/client.go b/server/gitea/client.go index c63ee21..51647ba 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -45,6 +45,8 @@ type Client struct { sdkClient *gitea.Client responseCache cache.SetGetKey + giteaRoot string + followSymlinks bool supportLFS bool @@ -79,6 +81,8 @@ func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, follo sdkClient: sdk, responseCache: respCache, + giteaRoot: giteaRoot, + followSymlinks: followSymlinks, supportLFS: supportLFS, @@ -87,6 +91,10 @@ func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, follo }, err } +func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource string) string { + return path.Join(client.giteaRoot, targetOwner, targetRepo, "src/branch", branch, resource) +} + func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) if err != nil { diff --git a/server/handler.go b/server/handler.go deleted file mode 100644 index 894cd25..0000000 --- a/server/handler.go +++ /dev/null @@ -1,321 +0,0 @@ -package server - -import ( - "fmt" - "net/http" - "path" - "strings" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - - "codeberg.org/codeberg/pages/html" - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/context" - "codeberg.org/codeberg/pages/server/dns" - "codeberg.org/codeberg/pages/server/gitea" - "codeberg.org/codeberg/pages/server/upstream" - "codeberg.org/codeberg/pages/server/utils" - "codeberg.org/codeberg/pages/server/version" -) - -const ( - headerAccessControlAllowOrigin = "Access-Control-Allow-Origin" - headerAccessControlAllowMethods = "Access-Control-Allow-Methods" -) - -// Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain string, - giteaClient *gitea.Client, - giteaRoot, rawInfoPage string, - blacklistedPaths, allowedCorsDomains []string, - dnsLookupCache, canonicalDomainCache cache.SetGetKey, -) http.HandlerFunc { - return func(w http.ResponseWriter, req *http.Request) { - log := log.With().Strs("Handler", []string{string(req.Host), req.RequestURI}).Logger() - ctx := context.New(w, req) - - ctx.RespWriter.Header().Set("Server", "CodebergPages/"+version.Version) - - // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin - ctx.RespWriter.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") - - // Enable browser caching for up to 10 minutes - ctx.RespWriter.Header().Set("Cache-Control", "public, max-age=600") - - trimmedHost := utils.TrimHostPort(req.Host) - - // Add HSTS for RawDomain and MainDomainSuffix - if hsts := getHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { - ctx.RespWriter.Header().Set("Strict-Transport-Security", hsts) - } - - // Block all methods not required for static pages - if !ctx.IsMethod(http.MethodGet) && !ctx.IsMethod(http.MethodHead) && !ctx.IsMethod(http.MethodOptions) { - ctx.RespWriter.Header().Set("Allow", http.MethodGet+", "+http.MethodHead+", "+http.MethodOptions) // duplic 1 - ctx.String("Method not allowed", http.StatusMethodNotAllowed) - return - } - - // Block blacklisted paths (like ACME challenges) - for _, blacklistedPath := range blacklistedPaths { - if strings.HasPrefix(ctx.Path(), blacklistedPath) { - html.ReturnErrorPage(ctx, "requested blacklisted path", http.StatusForbidden) - return - } - } - - // Allow CORS for specified domains - allowCors := false - for _, allowedCorsDomain := range allowedCorsDomains { - if strings.EqualFold(trimmedHost, allowedCorsDomain) { - allowCors = true - break - } - } - if allowCors { - ctx.RespWriter.Header().Set(headerAccessControlAllowOrigin, "*") - ctx.RespWriter.Header().Set(headerAccessControlAllowMethods, http.MethodGet+", "+http.MethodHead) - } - - ctx.RespWriter.Header().Set("Allow", http.MethodGet+", "+http.MethodHead+", "+http.MethodOptions) // duplic 1 - if ctx.IsMethod(http.MethodOptions) { - ctx.RespWriter.WriteHeader(http.StatusNoContent) - return - } - - // Prepare request information to Gitea - var targetOwner, targetRepo, targetBranch, targetPath string - targetOptions := &upstream.Options{ - TryIndexPages: true, - } - - // tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, it will - // also disallow search indexing and add a Link header to the canonical URL. - // TODO: move into external func to not alert vars indirectly - tryBranch := func(log zerolog.Logger, repo, branch string, _path []string, canonicalLink string) bool { - if repo == "" { - log.Debug().Msg("tryBranch: repo is empty") - return false - } - - // Replace "~" to "/" so we can access branch that contains slash character - // Branch name cannot contain "~" so doing this is okay - branch = strings.ReplaceAll(branch, "~", "/") - - // Check if the branch exists, otherwise treat it as a file path - branchTimestampResult := upstream.GetBranchTimestamp(giteaClient, targetOwner, repo, branch) - if branchTimestampResult == nil { - log.Debug().Msg("tryBranch: branch doesn't exist") - return false - } - - // Branch exists, use it - targetRepo = repo - targetPath = path.Join(_path...) - targetBranch = branchTimestampResult.Branch - - targetOptions.BranchTimestamp = branchTimestampResult.Timestamp - - if canonicalLink != "" { - // Hide from search machines & add canonical link - ctx.RespWriter.Header().Set("X-Robots-Tag", "noarchive, noindex") - ctx.RespWriter.Header().Set("Link", - strings.NewReplacer("%b", targetBranch, "%p", targetPath).Replace(canonicalLink)+ - "; rel=\"canonical\"", - ) - } - - log.Debug().Msg("tryBranch: true") - return true - } - - log.Debug().Msg("preparations") - if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { - // Serve raw content from RawDomain - log.Debug().Msg("raw domain") - - targetOptions.TryIndexPages = false - targetOptions.ServeRaw = true - - pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") - if len(pathElements) < 2 { - // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required - ctx.Redirect(rawInfoPage, http.StatusTemporaryRedirect) - return - } - targetOwner = pathElements[0] - targetRepo = pathElements[1] - - // raw.codeberg.org/example/myrepo/@main/index.html - if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { - log.Debug().Msg("raw domain preparations, now trying with specified branch") - if tryBranch(log, - targetRepo, pathElements[2][1:], pathElements[3:], - giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", - ) { - log.Debug().Msg("tryBranch, now trying upstream 1") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - return - } - log.Debug().Msg("missing branch info") - html.ReturnErrorPage(ctx, "missing branch info", http.StatusFailedDependency) - return - } - - log.Debug().Msg("raw domain preparations, now trying with default branch") - tryBranch(log, - targetRepo, "", pathElements[2:], - giteaRoot+"/"+targetOwner+"/"+targetRepo+"/src/branch/%b/%p", - ) - log.Debug().Msg("tryBranch, now trying upstream 2") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - return - - } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { - // Serve pages from subdomains of MainDomainSuffix - log.Debug().Msg("main domain suffix") - - pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") - targetOwner = strings.TrimSuffix(trimmedHost, mainDomainSuffix) - targetRepo = pathElements[0] - targetPath = strings.Trim(strings.Join(pathElements[1:], "/"), "/") - - if targetOwner == "www" { - // www.codeberg.page redirects to codeberg.page // TODO: rm hardcoded - use cname? - ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), http.StatusPermanentRedirect) - return - } - - // Check if the first directory is a repo with the second directory as a branch - // example.codeberg.page/myrepo/@main/index.html - if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { - if targetRepo == "pages" { - // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... - ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), http.StatusTemporaryRedirect) - return - } - - log.Debug().Msg("main domain preparations, now trying with specified repo & branch") - branch := pathElements[1][1:] - if tryBranch(log, - pathElements[0], branch, pathElements[2:], - "/"+pathElements[0]+"/%p", - ) { - log.Debug().Msg("tryBranch, now trying upstream 3") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - } else { - html.ReturnErrorPage(ctx, - fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", branch, targetOwner, targetRepo), - http.StatusFailedDependency) - } - return - } - - // Check if the first directory is a branch for the "pages" repo - // example.codeberg.page/@main/index.html - if strings.HasPrefix(pathElements[0], "@") { - log.Debug().Msg("main domain preparations, now trying with specified branch") - branch := pathElements[0][1:] - if tryBranch(log, - "pages", branch, pathElements[1:], "/%p") { - log.Debug().Msg("tryBranch, now trying upstream 4") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, "pages", targetBranch, targetPath, - canonicalDomainCache) - } else { - html.ReturnErrorPage(ctx, - fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", branch, targetOwner, "pages"), - http.StatusFailedDependency) - } - return - } - - // Check if the first directory is a repo with a "pages" branch - // example.codeberg.page/myrepo/index.html - // example.codeberg.page/pages/... is not allowed here. - log.Debug().Msg("main domain preparations, now trying with specified repo") - if pathElements[0] != "pages" && tryBranch(log, - pathElements[0], "pages", pathElements[1:], "") { - log.Debug().Msg("tryBranch, now trying upstream 5") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - return - } - - // Try to use the "pages" repo on its default branch - // example.codeberg.page/index.html - log.Debug().Msg("main domain preparations, now trying with default repo/branch") - if tryBranch(log, - "pages", "", pathElements, "") { - log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - return - } - - // Couldn't find a valid repo/branch - html.ReturnErrorPage(ctx, - fmt.Sprintf("couldn't find a valid repo[%s]/branch[%s]", targetRepo, targetBranch), - http.StatusFailedDependency) - return - } else { - trimmedHostStr := string(trimmedHost) - - // Serve pages from custom domains - targetOwner, targetRepo, targetBranch = dns.GetTargetFromDNS(trimmedHostStr, string(mainDomainSuffix), dnsLookupCache) - if targetOwner == "" { - html.ReturnErrorPage(ctx, - "could not obtain repo owner from custom domain", - http.StatusFailedDependency) - return - } - - pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") - canonicalLink := "" - if strings.HasPrefix(pathElements[0], "@") { - targetBranch = pathElements[0][1:] - pathElements = pathElements[1:] - canonicalLink = "/%p" - } - - // Try to use the given repo on the given branch or the default branch - log.Debug().Msg("custom domain preparations, now trying with details from DNS") - if tryBranch(log, - targetRepo, targetBranch, pathElements, canonicalLink) { - canonicalDomain, valid := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, trimmedHostStr, string(mainDomainSuffix), canonicalDomainCache) - if !valid { - html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) - return - } else if canonicalDomain != trimmedHostStr { - // only redirect if the target is also a codeberg page! - targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) - if targetOwner != "" { - ctx.Redirect("https://"+canonicalDomain+string(ctx.Path()), http.StatusTemporaryRedirect) - return - } - - html.ReturnErrorPage(ctx, "target is no codeberg page", http.StatusFailedDependency) - return - } - - log.Debug().Msg("tryBranch, now trying upstream 7") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, - targetOptions, targetOwner, targetRepo, targetBranch, targetPath, - canonicalDomainCache) - return - } - - html.ReturnErrorPage(ctx, "could not find target for custom domain", http.StatusFailedDependency) - return - } - } -} diff --git a/server/handler/handler.go b/server/handler/handler.go new file mode 100644 index 0000000..b42751e --- /dev/null +++ b/server/handler/handler.go @@ -0,0 +1,111 @@ +package handler + +import ( + "net/http" + "strings" + + "github.com/rs/zerolog/log" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/version" +) + +const ( + headerAccessControlAllowOrigin = "Access-Control-Allow-Origin" + headerAccessControlAllowMethods = "Access-Control-Allow-Methods" +) + +// Handler handles a single HTTP request to the web server. +func Handler(mainDomainSuffix, rawDomain string, + giteaClient *gitea.Client, + rawInfoPage string, + blacklistedPaths, allowedCorsDomains []string, + dnsLookupCache, canonicalDomainCache cache.SetGetKey, +) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + log := log.With().Strs("Handler", []string{string(req.Host), req.RequestURI}).Logger() + ctx := context.New(w, req) + + ctx.RespWriter.Header().Set("Server", "CodebergPages/"+version.Version) + + // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin + ctx.RespWriter.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + + // Enable browser caching for up to 10 minutes + ctx.RespWriter.Header().Set("Cache-Control", "public, max-age=600") + + trimmedHost := ctx.TrimHostPort() + + // Add HSTS for RawDomain and MainDomainSuffix + if hsts := getHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { + ctx.RespWriter.Header().Set("Strict-Transport-Security", hsts) + } + + // Handle all http methods + ctx.RespWriter.Header().Set("Allow", http.MethodGet+", "+http.MethodHead+", "+http.MethodOptions) + switch ctx.Req.Method { + case http.MethodOptions: + // return Allow header + ctx.RespWriter.WriteHeader(http.StatusNoContent) + return + case http.MethodGet, + http.MethodHead: + // end switch case and handle allowed requests + break + default: + // Block all methods not required for static pages + ctx.String("Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Block blacklisted paths (like ACME challenges) + for _, blacklistedPath := range blacklistedPaths { + if strings.HasPrefix(ctx.Path(), blacklistedPath) { + html.ReturnErrorPage(ctx, "requested blacklisted path", http.StatusForbidden) + return + } + } + + // Allow CORS for specified domains + allowCors := false + for _, allowedCorsDomain := range allowedCorsDomains { + if strings.EqualFold(trimmedHost, allowedCorsDomain) { + allowCors = true + break + } + } + if allowCors { + ctx.RespWriter.Header().Set(headerAccessControlAllowOrigin, "*") + ctx.RespWriter.Header().Set(headerAccessControlAllowMethods, http.MethodGet+", "+http.MethodHead) + } + + // Prepare request information to Gitea + pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") + + if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { + log.Debug().Msg("raw domain request detecded") + handleRaw(log, ctx, giteaClient, + mainDomainSuffix, rawInfoPage, + trimmedHost, + pathElements, + canonicalDomainCache) + } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { + log.Debug().Msg("subdomain request detecded") + handleSubDomain(log, ctx, giteaClient, + mainDomainSuffix, + trimmedHost, + pathElements, + canonicalDomainCache) + } else { + log.Debug().Msg("custom domain request detecded") + handleCustomDomain(log, ctx, giteaClient, + mainDomainSuffix, + trimmedHost, + pathElements, + dnsLookupCache, canonicalDomainCache) + } + } +} diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go new file mode 100644 index 0000000..bec3b46 --- /dev/null +++ b/server/handler/handler_custom_domain.go @@ -0,0 +1,71 @@ +package handler + +import ( + "net/http" + "path" + "strings" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/dns" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/upstream" + "github.com/rs/zerolog" +) + +func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, + mainDomainSuffix string, + trimmedHost string, + pathElements []string, + dnsLookupCache, canonicalDomainCache cache.SetGetKey, +) { + // Serve pages from custom domains + targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, string(mainDomainSuffix), dnsLookupCache) + if targetOwner == "" { + html.ReturnErrorPage(ctx, + "could not obtain repo owner from custom domain", + http.StatusFailedDependency) + return + } + + pathParts := pathElements + canonicalLink := false + if strings.HasPrefix(pathElements[0], "@") { + targetBranch = pathElements[0][1:] + pathParts = pathElements[1:] + canonicalLink = true + } + + // Try to use the given repo on the given branch or the default branch + log.Debug().Msg("custom domain preparations, now trying with details from DNS") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: targetRepo, + TargetBranch: targetBranch, + TargetPath: path.Join(pathParts...), + }, canonicalLink); works { + canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, string(mainDomainSuffix), canonicalDomainCache) + if !valid { + html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) + return + } else if canonicalDomain != trimmedHost { + // only redirect if the target is also a codeberg page! + targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) + if targetOwner != "" { + ctx.Redirect("https://"+canonicalDomain+string(targetOpt.TargetPath), http.StatusTemporaryRedirect) + return + } + + html.ReturnErrorPage(ctx, "target is no codeberg page", http.StatusFailedDependency) + return + } + + log.Debug().Msg("tryBranch, now trying upstream 7") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + return + } + + html.ReturnErrorPage(ctx, "could not find target for custom domain", http.StatusFailedDependency) +} diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go new file mode 100644 index 0000000..5e974da --- /dev/null +++ b/server/handler/handler_raw_domain.go @@ -0,0 +1,67 @@ +package handler + +import ( + "fmt" + "net/http" + "path" + "strings" + + "github.com/rs/zerolog" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/upstream" +) + +func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, + mainDomainSuffix, rawInfoPage string, + trimmedHost string, + pathElements []string, + canonicalDomainCache cache.SetGetKey, +) { + // Serve raw content from RawDomain + log.Debug().Msg("raw domain") + + if len(pathElements) < 2 { + // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required + ctx.Redirect(rawInfoPage, http.StatusTemporaryRedirect) + return + } + + // raw.codeberg.org/example/myrepo/@main/index.html + if len(pathElements) > 2 && strings.HasPrefix(pathElements[2], "@") { + log.Debug().Msg("raw domain preparations, now trying with specified branch") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + ServeRaw: true, + TargetOwner: pathElements[0], + TargetRepo: pathElements[1], + TargetBranch: pathElements[2][1:], + TargetPath: path.Join(pathElements[3:]...), + }, true); works { + log.Trace().Msg("tryUpstream: serve raw domain with specified branch") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + return + } + log.Debug().Msg("missing branch info") + html.ReturnErrorPage(ctx, "missing branch info", http.StatusFailedDependency) + return + } + + log.Debug().Msg("raw domain preparations, now trying with default branch") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: false, + ServeRaw: true, + TargetOwner: pathElements[0], + TargetRepo: pathElements[1], + TargetPath: path.Join(pathElements[2:]...), + }, true); works { + log.Trace().Msg("tryUpstream: serve raw domain with default branch") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + } else { + html.ReturnErrorPage(ctx, + fmt.Sprintf("raw domain could not find repo '%s/%s' or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo), + http.StatusNotFound) + } +} diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go new file mode 100644 index 0000000..df42d61 --- /dev/null +++ b/server/handler/handler_sub_domain.go @@ -0,0 +1,120 @@ +package handler + +import ( + "fmt" + "net/http" + "path" + "strings" + + "github.com/rs/zerolog" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/upstream" +) + +func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, + mainDomainSuffix string, + trimmedHost string, + pathElements []string, + canonicalDomainCache cache.SetGetKey, +) { + // Serve pages from subdomains of MainDomainSuffix + log.Debug().Msg("main domain suffix") + + targetOwner := strings.TrimSuffix(trimmedHost, mainDomainSuffix) + targetRepo := pathElements[0] + + if targetOwner == "www" { + // www.codeberg.page redirects to codeberg.page // TODO: rm hardcoded - use cname? + ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), http.StatusPermanentRedirect) + return + } + + // Check if the first directory is a repo with the second directory as a branch + // example.codeberg.page/myrepo/@main/index.html + if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { + if targetRepo == "pages" { + // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... + ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), http.StatusTemporaryRedirect) + return + } + + log.Debug().Msg("main domain preparations, now trying with specified repo & branch") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: pathElements[0], + TargetBranch: pathElements[1][1:], + TargetPath: path.Join(pathElements[2:]...), + }, true); works { + log.Trace().Msg("tryUpstream: serve with specified repo and branch") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + } else { + html.ReturnErrorPage(ctx, + fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + http.StatusFailedDependency) + } + return + } + + // Check if the first directory is a branch for the "pages" repo + // example.codeberg.page/@main/index.html + if strings.HasPrefix(pathElements[0], "@") { + log.Debug().Msg("main domain preparations, now trying with specified branch") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: "pages", + TargetBranch: pathElements[0][1:], + TargetPath: path.Join(pathElements[1:]...), + }, true); works { + log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + } else { + html.ReturnErrorPage(ctx, + fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + http.StatusFailedDependency) + } + return + } + + // Check if the first directory is a repo with a "pages" branch + // example.codeberg.page/myrepo/index.html + // example.codeberg.page/pages/... is not allowed here. + log.Debug().Msg("main domain preparations, now trying with specified repo") + if pathElements[0] != "pages" { + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: pathElements[0], + TargetBranch: "pages", + TargetPath: path.Join(pathElements[1:]...), + }, false); works { + log.Debug().Msg("tryBranch, now trying upstream 5") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + return + } + } + + // Try to use the "pages" repo on its default branch + // example.codeberg.page/index.html + log.Debug().Msg("main domain preparations, now trying with default repo/branch") + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: "pages", + TargetPath: path.Join(pathElements...), + }, false); works { + log.Debug().Msg("tryBranch, now trying upstream 6") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + return + } + + // Couldn't find a valid repo/branch + html.ReturnErrorPage(ctx, + fmt.Sprintf("couldn't find a valid repo[%s]", targetRepo), + http.StatusFailedDependency) +} diff --git a/server/handler_test.go b/server/handler/handler_test.go similarity index 85% rename from server/handler_test.go rename to server/handler/handler_test.go index c0aca14..f5538c9 100644 --- a/server/handler_test.go +++ b/server/handler/handler_test.go @@ -1,4 +1,4 @@ -package server +package handler import ( "net/http/httptest" @@ -11,12 +11,11 @@ import ( ) func TestHandlerPerformance(t *testing.T) { - giteaRoot := "https://codeberg.org" - giteaClient, _ := gitea.NewClient(giteaRoot, "", cache.NewKeyValueCache(), false, false) + giteaClient, _ := gitea.NewClient("https://codeberg.org", "", cache.NewKeyValueCache(), false, false) testHandler := Handler( "codeberg.page", "raw.codeberg.org", giteaClient, - giteaRoot, "https://docs.codeberg.org/pages/raw-content/", + "https://docs.codeberg.org/pages/raw-content/", []string{"/.well-known/acme-challenge/"}, []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, cache.NewKeyValueCache(), diff --git a/server/helpers.go b/server/handler/hsts.go similarity index 96% rename from server/helpers.go rename to server/handler/hsts.go index 7c898cd..1ab73ae 100644 --- a/server/helpers.go +++ b/server/handler/hsts.go @@ -1,4 +1,4 @@ -package server +package handler import ( "strings" diff --git a/server/handler/try.go b/server/handler/try.go new file mode 100644 index 0000000..b9adb7b --- /dev/null +++ b/server/handler/try.go @@ -0,0 +1,76 @@ +package handler + +import ( + "net/http" + "strings" + + "github.com/rs/zerolog" + + "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/upstream" +) + +// tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. +func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, + mainDomainSuffix, trimmedHost string, + options *upstream.Options, + canonicalDomainCache cache.SetGetKey, +) { + // check if a canonical domain exists on a request on MainDomain + if strings.HasSuffix(trimmedHost, mainDomainSuffix) { + canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", string(mainDomainSuffix), canonicalDomainCache) + if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { + canonicalPath := ctx.Req.RequestURI + if options.TargetRepo != "pages" { + path := strings.SplitN(canonicalPath, "/", 3) + if len(path) >= 3 { + canonicalPath = "/" + path[2] + } + } + ctx.Redirect("https://"+canonicalDomain+canonicalPath, http.StatusTemporaryRedirect) + return + } + } + + // add host for debugging + options.Host = string(trimmedHost) + + // Try to request the file from the Gitea API + if !options.Upstream(ctx, giteaClient) { + html.ReturnErrorPage(ctx, "", ctx.StatusCode) + } +} + +// tryBranch checks if a branch exists and populates the target variables. If canonicalLink is non-empty, +// it will also disallow search indexing and add a Link header to the canonical URL. +func tryBranch(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, + targetOptions *upstream.Options, canonicalLink bool, +) (*upstream.Options, bool) { + if targetOptions.TargetOwner == "" || targetOptions.TargetRepo == "" { + log.Debug().Msg("tryBranch: owner or repo is empty") + return nil, false + } + + // Replace "~" to "/" so we can access branch that contains slash character + // Branch name cannot contain "~" so doing this is okay + targetOptions.TargetBranch = strings.ReplaceAll(targetOptions.TargetBranch, "~", "/") + + // Check if the branch exists, otherwise treat it as a file path + branchExist, _ := targetOptions.GetBranchTimestamp(giteaClient) + if !branchExist { + log.Debug().Msg("tryBranch: branch doesn't exist") + return nil, false + } + + if canonicalLink { + // Hide from search machines & add canonical link + ctx.RespWriter.Header().Set("X-Robots-Tag", "noarchive, noindex") + ctx.RespWriter.Header().Set("Link", targetOptions.ContentWebLink(giteaClient)+"; rel=\"canonical\"") + } + + log.Debug().Msg("tryBranch: true") + return targetOptions, true +} diff --git a/server/try.go b/server/try.go deleted file mode 100644 index 135c1e0..0000000 --- a/server/try.go +++ /dev/null @@ -1,49 +0,0 @@ -package server - -import ( - "net/http" - "strings" - - "codeberg.org/codeberg/pages/html" - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/context" - "codeberg.org/codeberg/pages/server/gitea" - "codeberg.org/codeberg/pages/server/upstream" -) - -// tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. -func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, - mainDomainSuffix, trimmedHost string, - - targetOptions *upstream.Options, - targetOwner, targetRepo, targetBranch, targetPath string, - - canonicalDomainCache cache.SetGetKey, -) { - // check if a canonical domain exists on a request on MainDomain - if strings.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := upstream.CheckCanonicalDomain(giteaClient, targetOwner, targetRepo, targetBranch, "", string(mainDomainSuffix), canonicalDomainCache) - if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { - canonicalPath := ctx.Req.RequestURI - if targetRepo != "pages" { - path := strings.SplitN(canonicalPath, "/", 3) - if len(path) >= 3 { - canonicalPath = "/" + path[2] - } - } - ctx.Redirect("https://"+canonicalDomain+canonicalPath, http.StatusTemporaryRedirect) - return - } - } - - targetOptions.TargetOwner = targetOwner - targetOptions.TargetRepo = targetRepo - targetOptions.TargetBranch = targetBranch - targetOptions.TargetPath = targetPath - targetOptions.Host = string(trimmedHost) - - // Try to request the file from the Gitea API - if !targetOptions.Upstream(ctx, giteaClient) { - html.ReturnErrorPage(ctx, "", ctx.StatusCode) - } -} diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 6ad6506..0e29673 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -16,12 +16,12 @@ var canonicalDomainCacheTimeout = 15 * time.Minute const canonicalDomainConfig = ".domains" // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). -func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, targetBranch, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (string, bool) { +func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (string, bool) { var ( domains []string valid bool ) - if cachedValue, ok := canonicalDomainCache.Get(targetOwner + "/" + targetRepo + "/" + targetBranch); ok { + if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { domains = cachedValue.([]string) for _, domain := range domains { if domain == actualDomain { @@ -30,7 +30,7 @@ func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, ta } } } else { - body, err := giteaClient.GiteaRawContent(targetOwner, targetRepo, targetBranch, canonicalDomainConfig) + body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) if err == nil { for _, domain := range strings.Split(string(body), "\n") { domain = strings.ToLower(domain) @@ -45,16 +45,16 @@ func CheckCanonicalDomain(giteaClient *gitea.Client, targetOwner, targetRepo, ta } } } else { - log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, targetOwner, targetRepo) + log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) } - domains = append(domains, targetOwner+mainDomainSuffix) + domains = append(domains, o.TargetOwner+mainDomainSuffix) if domains[len(domains)-1] == actualDomain { valid = true } - if targetRepo != "" && targetRepo != "pages" { - domains[len(domains)-1] += "/" + targetRepo + if o.TargetRepo != "" && o.TargetRepo != "pages" { + domains[len(domains)-1] += "/" + o.TargetRepo } - _ = canonicalDomainCache.Set(targetOwner+"/"+targetRepo+"/"+targetBranch, domains, canonicalDomainCacheTimeout) + _ = canonicalDomainCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, domains, canonicalDomainCacheTimeout) } return domains[0], valid } diff --git a/server/upstream/header.go b/server/upstream/header.go new file mode 100644 index 0000000..9575a3f --- /dev/null +++ b/server/upstream/header.go @@ -0,0 +1,28 @@ +package upstream + +import ( + "net/http" + "time" + + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" +) + +// setHeader set values to response header +func (o *Options) setHeader(ctx *context.Context, header http.Header) { + if eTag := header.Get(gitea.ETagHeader); eTag != "" { + ctx.RespWriter.Header().Set(gitea.ETagHeader, eTag) + } + if cacheIndicator := header.Get(gitea.PagesCacheIndicatorHeader); cacheIndicator != "" { + ctx.RespWriter.Header().Set(gitea.PagesCacheIndicatorHeader, cacheIndicator) + } + if length := header.Get(gitea.ContentLengthHeader); length != "" { + ctx.RespWriter.Header().Set(gitea.ContentLengthHeader, length) + } + if mime := header.Get(gitea.ContentTypeHeader); mime == "" || o.ServeRaw { + ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, rawMime) + } else { + ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) + } + ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(time.RFC1123)) +} diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 6bc23c8..428976b 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -2,35 +2,46 @@ package upstream import ( "errors" + "fmt" "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/gitea" ) -// GetBranchTimestamp finds the default branch (if branch is "") and returns the last modification time of the branch -// (or nil if the branch doesn't exist) -func GetBranchTimestamp(giteaClient *gitea.Client, owner, repo, branch string) *gitea.BranchTimestamp { - log := log.With().Strs("BranchInfo", []string{owner, repo, branch}).Logger() +// GetBranchTimestamp finds the default branch (if branch is "") and save branch and it's last modification time to Options +func (o *Options) GetBranchTimestamp(giteaClient *gitea.Client) (bool, error) { + log := log.With().Strs("BranchInfo", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch}).Logger() - if len(branch) == 0 { + if len(o.TargetBranch) == 0 { // Get default branch - defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(owner, repo) + defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(o.TargetOwner, o.TargetRepo) if err != nil { log.Err(err).Msg("Could't fetch default branch from repository") - return nil + return false, err } log.Debug().Msgf("Succesfully fetched default branch %q from Gitea", defaultBranch) - branch = defaultBranch + o.TargetBranch = defaultBranch } - timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(owner, repo, branch) + timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(o.TargetOwner, o.TargetRepo, o.TargetBranch) if err != nil { if !errors.Is(err, gitea.ErrorNotFound) { log.Error().Err(err).Msg("Could not get latest commit's timestamp from branch") } - return nil + return false, err } + + if timestamp == nil || timestamp.Branch == "" { + return false, fmt.Errorf("empty response") + } + log.Debug().Msgf("Succesfully fetched latest commit's timestamp from branch: %#v", timestamp) - return timestamp + o.BranchTimestamp = timestamp.Timestamp + o.TargetBranch = timestamp.Branch + return true, nil +} + +func (o *Options) ContentWebLink(giteaClient *gitea.Client) string { + return giteaClient.ContentWebLink(o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath) + "; rel=\"canonical\"" } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index d37c35e..b76b8e6 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -34,10 +34,10 @@ var upstreamNotFoundPages = []string{ // Options provides various options for the upstream request. type Options struct { - TargetOwner, - TargetRepo, - TargetBranch, - TargetPath, + TargetOwner string + TargetRepo string + TargetBranch string + TargetPath string // Used for debugging purposes. Host string @@ -62,16 +62,22 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin // Check if the branch exists and when it was modified if o.BranchTimestamp.IsZero() { - branch := GetBranchTimestamp(giteaClient, o.TargetOwner, o.TargetRepo, o.TargetBranch) - - if branch == nil || branch.Branch == "" { + branchExist, err := o.GetBranchTimestamp(giteaClient) + // handle 404 + if err != nil && errors.Is(err, gitea.ErrorNotFound) || !branchExist { html.ReturnErrorPage(ctx, - fmt.Sprintf("could not get timestamp of branch %q", o.TargetBranch), + fmt.Sprintf("branch %q for '%s/%s' not found", o.TargetBranch, o.TargetOwner, o.TargetRepo), + http.StatusNotFound) + return true + } + + // handle unexpected errors + if err != nil { + html.ReturnErrorPage(ctx, + fmt.Sprintf("could not get timestamp of branch %q: %v", o.TargetBranch, err), http.StatusFailedDependency) return true } - o.TargetBranch = branch.Branch - o.BranchTimestamp = branch.Timestamp } // Check if the browser has a cached version @@ -172,21 +178,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin } // Set ETag & MIME - if eTag := header.Get(gitea.ETagHeader); eTag != "" { - ctx.RespWriter.Header().Set(gitea.ETagHeader, eTag) - } - if cacheIndicator := header.Get(gitea.PagesCacheIndicatorHeader); cacheIndicator != "" { - ctx.RespWriter.Header().Set(gitea.PagesCacheIndicatorHeader, cacheIndicator) - } - if length := header.Get(gitea.ContentLengthHeader); length != "" { - ctx.RespWriter.Header().Set(gitea.ContentLengthHeader, length) - } - if mime := header.Get(gitea.ContentTypeHeader); mime == "" || o.ServeRaw { - ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, rawMime) - } else { - ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) - } - ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(time.RFC1123)) + o.setHeader(ctx, header) log.Debug().Msg("Prepare response") From b6d0a04b217f34705cb8cb6ba89107c76a87bfb1 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 21:04:34 +0100 Subject: [PATCH 168/320] refactor: rm not needed type conversion --- server/handler/handler_custom_domain.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index bec3b46..5ea7649 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -21,7 +21,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g dnsLookupCache, canonicalDomainCache cache.SetGetKey, ) { // Serve pages from custom domains - targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, string(mainDomainSuffix), dnsLookupCache) + targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, dnsLookupCache) if targetOwner == "" { html.ReturnErrorPage(ctx, "could not obtain repo owner from custom domain", @@ -46,13 +46,13 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g TargetBranch: targetBranch, TargetPath: path.Join(pathParts...), }, canonicalLink); works { - canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, string(mainDomainSuffix), canonicalDomainCache) + canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHost { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix), dnsLookupCache) + targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, dnsLookupCache) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+string(targetOpt.TargetPath), http.StatusTemporaryRedirect) return From aa90356f0a4d48556a749e4e3677aa5e7641c4d8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 21:10:16 +0100 Subject: [PATCH 169/320] use a const for defaultPagesRepo --- server/handler/handler_sub_domain.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index df42d61..8190cd9 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -15,6 +15,8 @@ import ( "codeberg.org/codeberg/pages/server/upstream" ) +const defaultPagesRepo = "pages" + func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix string, trimmedHost string, @@ -36,7 +38,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite // Check if the first directory is a repo with the second directory as a branch // example.codeberg.page/myrepo/@main/index.html if len(pathElements) > 1 && strings.HasPrefix(pathElements[1], "@") { - if targetRepo == "pages" { + if targetRepo == defaultPagesRepo { // example.codeberg.org/pages/@... redirects to example.codeberg.org/@... ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), http.StatusTemporaryRedirect) return @@ -60,14 +62,14 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite return } - // Check if the first directory is a branch for the "pages" repo + // Check if the first directory is a branch for the defaultPagesRepo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { log.Debug().Msg("main domain preparations, now trying with specified branch") if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ TryIndexPages: true, TargetOwner: targetOwner, - TargetRepo: "pages", + TargetRepo: defaultPagesRepo, TargetBranch: pathElements[0][1:], TargetPath: path.Join(pathElements[1:]...), }, true); works { @@ -81,16 +83,16 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite return } - // Check if the first directory is a repo with a "pages" branch + // Check if the first directory is a repo with a defaultPagesRepo branch // example.codeberg.page/myrepo/index.html // example.codeberg.page/pages/... is not allowed here. log.Debug().Msg("main domain preparations, now trying with specified repo") - if pathElements[0] != "pages" { + if pathElements[0] != defaultPagesRepo { if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ TryIndexPages: true, TargetOwner: targetOwner, TargetRepo: pathElements[0], - TargetBranch: "pages", + TargetBranch: defaultPagesRepo, TargetPath: path.Join(pathElements[1:]...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 5") @@ -99,13 +101,13 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite } } - // Try to use the "pages" repo on its default branch + // Try to use the defaultPagesRepo on its default branch // example.codeberg.page/index.html log.Debug().Msg("main domain preparations, now trying with default repo/branch") if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ TryIndexPages: true, TargetOwner: targetOwner, - TargetRepo: "pages", + TargetRepo: defaultPagesRepo, TargetPath: path.Join(pathElements...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 6") From c827a28dd829974af387665dc7e0db92283ed930 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 21:13:13 +0100 Subject: [PATCH 170/320] defaultPagesBranch --- server/handler/handler_sub_domain.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 8190cd9..59d1bbc 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -16,6 +16,7 @@ import ( ) const defaultPagesRepo = "pages" +const defaultPagesBranch = "pages" func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix string, @@ -92,7 +93,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TryIndexPages: true, TargetOwner: targetOwner, TargetRepo: pathElements[0], - TargetBranch: defaultPagesRepo, + TargetBranch: defaultPagesBranch, TargetPath: path.Join(pathElements[1:]...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 5") From 4565481643dfd9014a674cd5a182a6c42e820fe0 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 12 Nov 2022 21:16:11 +0100 Subject: [PATCH 171/320] refactor: finish use default const for defaultPagesBranch and defaultPagesRepo --- server/handler/handler.go | 2 ++ server/handler/handler_sub_domain.go | 3 --- server/handler/try.go | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server/handler/handler.go b/server/handler/handler.go index b42751e..1a0a285 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -16,6 +16,8 @@ import ( const ( headerAccessControlAllowOrigin = "Access-Control-Allow-Origin" headerAccessControlAllowMethods = "Access-Control-Allow-Methods" + defaultPagesRepo = "pages" + defaultPagesBranch = "pages" ) // Handler handles a single HTTP request to the web server. diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 59d1bbc..73177b6 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -15,9 +15,6 @@ import ( "codeberg.org/codeberg/pages/server/upstream" ) -const defaultPagesRepo = "pages" -const defaultPagesBranch = "pages" - func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix string, trimmedHost string, diff --git a/server/handler/try.go b/server/handler/try.go index b9adb7b..bae3c44 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -24,7 +24,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", string(mainDomainSuffix), canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { canonicalPath := ctx.Req.RequestURI - if options.TargetRepo != "pages" { + if options.TargetRepo != defaultPagesRepo { path := strings.SplitN(canonicalPath, "/", 3) if len(path) >= 3 { canonicalPath = "/" + path[2] From 3c61a39864f087f1bb6305dbcd512bdd7ffb72fa Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 12 Nov 2022 22:25:20 +0100 Subject: [PATCH 172/320] Enable http/2 support (#137) As per [the documentation](https://pkg.go.dev/net/http#Serve), it doesn't enable HTTP2 by-default, unless we enable it via the `NextProtos` option. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/137 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/certificates/certificates.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 42620b1..11cf0df 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -117,6 +117,7 @@ func TLSConfig(mainDomainSuffix string, }, PreferServerCipherSuites: true, NextProtos: []string{ + "h2", "http/1.1", tlsalpn01.ACMETLS1Protocol, }, From f2f943c0d800c513025a2fecfb1a48729052fdc1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 15 Nov 2022 16:15:11 +0100 Subject: [PATCH 173/320] Remove unnecessary conversion (#139) - Remove unnecessary type conversion. - Enforce via CI Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/139 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- .golangci.yml | 20 ++++++++++ cmd/main.go | 2 +- integration/get_test.go | 2 +- integration/main_test.go | 2 +- server/certificates/certificates.go | 50 +++++++++++++------------ server/database/setup.go | 2 +- server/gitea/cache.go | 5 +-- server/handler/handler.go | 2 +- server/handler/handler_custom_domain.go | 2 +- server/handler/handler_sub_domain.go | 2 +- server/handler/try.go | 8 ++-- server/setup.go | 4 +- server/upstream/helper.go | 2 +- server/upstream/upstream.go | 4 +- 14 files changed, 64 insertions(+), 43 deletions(-) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..6d9b95a --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,20 @@ +linters-settings: + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - importShadow + - ifElseChain + - hugeParam + +linters: + enable: + - unconvert + - gocritic + +run: + timeout: 5m diff --git a/cmd/main.go b/cmd/main.go index 6ad1aa8..b72013a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -65,7 +65,7 @@ func Serve(ctx *cli.Context) error { } allowedCorsDomains := AllowedCorsDomains - if len(rawDomain) != 0 { + if rawDomain != "" { allowedCorsDomains = append(allowedCorsDomains, rawDomain) } diff --git a/integration/get_test.go b/integration/get_test.go index 8794651..81d8488 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -124,7 +124,7 @@ func TestLFSSupport(t *testing.T) { func TestGetOptions(t *testing.T) { log.Println("=== TestGetOptions ===") - req, _ := http.NewRequest(http.MethodOptions, "https://mock-pages.codeberg-test.org:4430/README.md", nil) + req, _ := http.NewRequest(http.MethodOptions, "https://mock-pages.codeberg-test.org:4430/README.md", http.NoBody) resp, err := getTestHTTPSClient().Do(req) assert.NoError(t, err) if !assert.NotNil(t, resp) { diff --git a/integration/main_test.go b/integration/main_test.go index 06d553f..406b33a 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -28,7 +28,7 @@ func TestMain(m *testing.M) { time.Sleep(10 * time.Second) - os.Exit(m.Run()) + m.Run() } func startServer(ctx context.Context) error { diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 11cf0df..8af4be5 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -53,17 +53,19 @@ func TLSConfig(mainDomainSuffix string, if info.SupportedProtos != nil { for _, proto := range info.SupportedProtos { - if proto == tlsalpn01.ACMETLS1Protocol { - challenge, ok := challengeCache.Get(sni) - if !ok { - return nil, errors.New("no challenge for this domain") - } - cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) - if err != nil { - return nil, err - } - return cert, nil + if proto != tlsalpn01.ACMETLS1Protocol { + continue } + + challenge, ok := challengeCache.Get(sni) + if !ok { + return nil, errors.New("no challenge for this domain") + } + cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) + if err != nil { + return nil, err + } + return cert, nil } } @@ -195,7 +197,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { // parse certificate from database - res, err := certDB.Get(string(sni)) + res, err := certDB.Get(sni) if err != nil { panic(err) // TODO: no panic } @@ -216,7 +218,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLi } // renew certificates 7 days before they expire - if !tlsCertificate.Leaf.NotAfter.After(time.Now().Add(7 * 24 * time.Hour)) { + if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { // TODO: add ValidUntil to custom res struct if res.CSR != nil && len(res.CSR) > 0 { // CSR stores the time when the renewal shall be tried again @@ -227,9 +229,9 @@ func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLi } go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{string(sni)}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { - log.Error().Msgf("Couldn't renew certificate for %s: %v", string(sni), err) + log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) } })() } @@ -262,7 +264,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re defer obtainLocks.Delete(name) if acmeClient == nil { - return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", string(mainDomainSuffix), keyDatabase), nil + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase), nil } // request actual cert @@ -305,12 +307,12 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) if err := keyDatabase.Put(name, renew); err != nil { - return mockCert(domains[0], err.Error(), string(mainDomainSuffix), keyDatabase), err + return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err } return tlsCertificate, nil } } - return mockCert(domains[0], err.Error(), string(mainDomainSuffix), keyDatabase), err + return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err } log.Debug().Msgf("Obtained certificate for %v", domains) @@ -408,7 +410,7 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits - mainCertBytes, err := certDB.Get(string(mainDomainSuffix)) + mainCertBytes, err := certDB.Get(mainDomainSuffix) if err != nil { return fmt.Errorf("cert database is not working") } @@ -452,7 +454,7 @@ func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Co } if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only") } @@ -479,7 +481,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - if err != nil || !tlsCertificates[0].NotAfter.After(now) { + if err != nil || tlsCertificates[0].NotAfter.Before(now) { err := certDB.Delete(string(key)) if err != nil { log.Error().Err(err).Msgf("Deleting expired certificate for %q failed", string(key)) @@ -501,18 +503,18 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } // update main cert - res, err := certDB.Get(string(mainDomainSuffix)) + res, err := certDB.Get(mainDomainSuffix) if err != nil { log.Error().Msgf("Couldn't get cert for domain %q", mainDomainSuffix) } else if res == nil { - log.Error().Msgf("Couldn't renew certificate for main domain %q expected main domain cert to exist, but it's missing - seems like the database is corrupted", string(mainDomainSuffix)) + log.Error().Msgf("Couldn't renew certificate for main domain %q expected main domain cert to exist, but it's missing - seems like the database is corrupted", mainDomainSuffix) } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) // renew main certificate 30 days before it expires - if !tlsCertificates[0].NotAfter.After(time.Now().Add(30 * 24 * time.Hour)) { + if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + string(mainDomainSuffix), string(mainDomainSuffix[1:])}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + _, err = obtainCert(mainDomainAcmeClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew certificate for main domain") } diff --git a/server/database/setup.go b/server/database/setup.go index 1c5a0af..097c63e 100644 --- a/server/database/setup.go +++ b/server/database/setup.go @@ -44,7 +44,7 @@ func (p aDB) Get(name string) (*certificate.Resource, error) { if resBytes == nil { return nil, nil } - if err = gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil { + if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil { return nil, err } return cert, nil diff --git a/server/gitea/cache.go b/server/gitea/cache.go index b11a370..85cbcde 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -42,9 +42,8 @@ func (f FileResponse) IsEmpty() bool { return len(f.Body) != 0 } -func (f FileResponse) createHttpResponse(cacheKey string) (http.Header, int) { - header := make(http.Header) - var statusCode int +func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) { + header = make(http.Header) if f.Exists { statusCode = http.StatusOK diff --git a/server/handler/handler.go b/server/handler/handler.go index 1a0a285..78301e9 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -28,7 +28,7 @@ func Handler(mainDomainSuffix, rawDomain string, dnsLookupCache, canonicalDomainCache cache.SetGetKey, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - log := log.With().Strs("Handler", []string{string(req.Host), req.RequestURI}).Logger() + log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() ctx := context.New(w, req) ctx.RespWriter.Header().Set("Server", "CodebergPages/"+version.Version) diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 5ea7649..2f98085 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -54,7 +54,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g // only redirect if the target is also a codeberg page! targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, dnsLookupCache) if targetOwner != "" { - ctx.Redirect("https://"+canonicalDomain+string(targetOpt.TargetPath), http.StatusTemporaryRedirect) + ctx.Redirect("https://"+canonicalDomain+targetOpt.TargetPath, http.StatusTemporaryRedirect) return } diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 73177b6..1d769d4 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -29,7 +29,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite if targetOwner == "www" { // www.codeberg.page redirects to codeberg.page // TODO: rm hardcoded - use cname? - ctx.Redirect("https://"+string(mainDomainSuffix[1:])+string(ctx.Path()), http.StatusPermanentRedirect) + ctx.Redirect("https://"+mainDomainSuffix[1:]+ctx.Path(), http.StatusPermanentRedirect) return } diff --git a/server/handler/try.go b/server/handler/try.go index bae3c44..5a09b91 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -21,8 +21,8 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, ) { // check if a canonical domain exists on a request on MainDomain if strings.HasSuffix(trimmedHost, mainDomainSuffix) { - canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", string(mainDomainSuffix), canonicalDomainCache) - if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], string(mainDomainSuffix)) { + canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", mainDomainSuffix, canonicalDomainCache) + if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix) { canonicalPath := ctx.Req.RequestURI if options.TargetRepo != defaultPagesRepo { path := strings.SplitN(canonicalPath, "/", 3) @@ -35,8 +35,8 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, } } - // add host for debugging - options.Host = string(trimmedHost) + // Add host for debugging. + options.Host = trimmedHost // Try to request the file from the Gitea API if !options.Upstream(ctx, giteaClient) { diff --git a/server/setup.go b/server/setup.go index e7194ed..282e692 100644 --- a/server/setup.go +++ b/server/setup.go @@ -15,13 +15,13 @@ func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFu return func(w http.ResponseWriter, req *http.Request) { ctx := context.New(w, req) if strings.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + string(strings.TrimPrefix(ctx.Path(), challengePath))) + challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) if !ok || challenge == nil { ctx.String("no challenge for this token", http.StatusNotFound) } ctx.String(challenge.(string)) } else { - ctx.Redirect("https://"+string(ctx.Host())+string(ctx.Path()), http.StatusMovedPermanently) + ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently) } } } diff --git a/server/upstream/helper.go b/server/upstream/helper.go index 428976b..a84d4f0 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -13,7 +13,7 @@ import ( func (o *Options) GetBranchTimestamp(giteaClient *gitea.Client) (bool, error) { log := log.With().Strs("BranchInfo", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch}).Logger() - if len(o.TargetBranch) == 0 { + if o.TargetBranch == "" { // Get default branch defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(o.TargetOwner, o.TargetRepo) if err != nil { diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index b76b8e6..7c3c848 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -82,8 +82,8 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin // Check if the browser has a cached version if ctx.Response() != nil { - if ifModifiedSince, err := time.Parse(time.RFC1123, string(ctx.Response().Header.Get(headerIfModifiedSince))); err == nil { - if !ifModifiedSince.Before(o.BranchTimestamp) { + if ifModifiedSince, err := time.Parse(time.RFC1123, ctx.Response().Header.Get(headerIfModifiedSince)); err == nil { + if ifModifiedSince.After(o.BranchTimestamp) { ctx.RespWriter.WriteHeader(http.StatusNotModified) log.Trace().Msg("check response against last modified: valid") return true From caeb1a4acb0d35c78988fcd4e30d9ac51847f13a Mon Sep 17 00:00:00 2001 From: jklippel Date: Tue, 22 Nov 2022 21:26:10 +0000 Subject: [PATCH 174/320] Return a 404 if there is no repository (#141) If no repository is found the user expects a 404 status code instead of a dependency failed status code (as it was before). Signed-off-by: Jan Klippel Fixes: https://codeberg.org/Codeberg/Community/issues/809 Co-authored-by: Jan Klippel Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/141 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: jklippel Co-committed-by: jklippel --- server/handler/handler_sub_domain.go | 4 ++-- server/handler/handler_test.go | 35 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 1d769d4..2a75e9f 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -115,6 +115,6 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite // Couldn't find a valid repo/branch html.ReturnErrorPage(ctx, - fmt.Sprintf("couldn't find a valid repo[%s]", targetRepo), - http.StatusFailedDependency) + fmt.Sprintf("could not find a valid repository[%s]", targetRepo), + http.StatusNotFound) } diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index f5538c9..626564a 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -23,26 +23,27 @@ func TestHandlerPerformance(t *testing.T) { ) testCase := func(uri string, status int) { - req := httptest.NewRequest("GET", uri, nil) - w := httptest.NewRecorder() + t.Run(uri, func(t *testing.T) { + req := httptest.NewRequest("GET", uri, nil) + w := httptest.NewRecorder() - log.Printf("Start: %v\n", time.Now()) - start := time.Now() - testHandler(w, req) - end := time.Now() - log.Printf("Done: %v\n", time.Now()) + log.Printf("Start: %v\n", time.Now()) + start := time.Now() + testHandler(w, req) + end := time.Now() + log.Printf("Done: %v\n", time.Now()) - resp := w.Result() + resp := w.Result() - if resp.StatusCode != status { - t.Errorf("request failed with status code %d", resp.StatusCode) - } else { - t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) - } + if resp.StatusCode != status { + t.Errorf("request failed with status code %d", resp.StatusCode) + } else { + t.Logf("request took %d milliseconds", end.Sub(start).Milliseconds()) + } + }) } - testCase("https://mondstern.codeberg.page/", 424) // TODO: expect 200 - testCase("https://mondstern.codeberg.page/", 424) // TODO: expect 200 - testCase("https://example.momar.xyz/", 424) // TODO: expect 200 - testCase("https://codeberg.page/", 424) // TODO: expect 200 + testCase("https://mondstern.codeberg.page/", 404) // TODO: expect 200 + testCase("https://codeberg.page/", 404) // TODO: expect 200 + testCase("https://example.momar.xyz/", 424) } From 5e72753e916d1076cfd772d3d6c7f86474c6c87a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 22 Nov 2022 22:30:53 +0100 Subject: [PATCH 175/320] ci: "docker-tag" use `tags` --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 20254fe..7e6d694 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -102,7 +102,7 @@ pipeline: registry: codeberg.org dockerfile: Dockerfile repo: codeberg.org/codeberg/pages-server - tag: [ latest, "${CI_COMMIT_TAG}" ] + tags: [ latest, "${CI_COMMIT_TAG}" ] username: from_secret: bot_user password: From dcf03fc078228a286384757c3501ab5df5306062 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 2 Dec 2022 15:25:25 +0000 Subject: [PATCH 176/320] Fix error page (#144) Co-authored-by: crapStone Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/144 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- html/error.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/html/error.go b/html/error.go index 826c42b..ac222c4 100644 --- a/html/error.go +++ b/html/error.go @@ -1,6 +1,7 @@ package html import ( + "html/template" "net/http" "strconv" "strings" @@ -39,7 +40,8 @@ func errorMessage(statusCode int) string { // TODO: use template engine func errorBody(statusCode int) string { - return strings.ReplaceAll(NotFoundPage, - "%status%", - strconv.Itoa(statusCode)+" "+errorMessage(statusCode)) + return template.HTMLEscapeString( + strings.ReplaceAll(NotFoundPage, + "%status%", + strconv.Itoa(statusCode)+" "+errorMessage(statusCode))) } From 9d769aeee7bdbdce03bbb0f6fb213d4b7d45a701 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 4 Dec 2022 21:24:58 +0000 Subject: [PATCH 177/320] Fix error page generation (#145) Co-authored-by: crapStone Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/145 Reviewed-by: Gusted Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- Justfile | 4 ++-- html/error.go | 31 +++++++++++++++++-------------- html/error_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 html/error_test.go diff --git a/Justfile b/Justfile index f7ea414..7d72fe8 100644 --- a/Justfile +++ b/Justfile @@ -38,10 +38,10 @@ tool-gofumpt: fi test: - go test -race codeberg.org/codeberg/pages/server/... + go test -race codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ test-run TEST: - go test -race -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... + go test -race -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ integration: go test -race -tags integration codeberg.org/codeberg/pages/integration/... diff --git a/html/error.go b/html/error.go index ac222c4..206b123 100644 --- a/html/error.go +++ b/html/error.go @@ -15,16 +15,27 @@ func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) { ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8") ctx.RespWriter.WriteHeader(statusCode) - if msg == "" { - msg = errorBody(statusCode) - } else { - // TODO: use template engine - msg = strings.ReplaceAll(strings.ReplaceAll(ErrorPage, "%message%", msg), "%status%", http.StatusText(statusCode)) - } + msg = generateResponse(msg, statusCode) _, _ = ctx.RespWriter.Write([]byte(msg)) } +// TODO: use template engine +func generateResponse(msg string, statusCode int) string { + if msg == "" { + msg = strings.ReplaceAll(NotFoundPage, + "%status%", + strconv.Itoa(statusCode)+" "+errorMessage(statusCode)) + } else { + msg = strings.ReplaceAll( + strings.ReplaceAll(ErrorPage, "%message%", template.HTMLEscapeString(msg)), + "%status%", + http.StatusText(statusCode)) + } + + return msg +} + func errorMessage(statusCode int) string { message := http.StatusText(statusCode) @@ -37,11 +48,3 @@ func errorMessage(statusCode int) string { return message } - -// TODO: use template engine -func errorBody(statusCode int) string { - return template.HTMLEscapeString( - strings.ReplaceAll(NotFoundPage, - "%status%", - strconv.Itoa(statusCode)+" "+errorMessage(statusCode))) -} diff --git a/html/error_test.go b/html/error_test.go new file mode 100644 index 0000000..f5da08c --- /dev/null +++ b/html/error_test.go @@ -0,0 +1,38 @@ +package html + +import ( + "net/http" + "strings" + "testing" +) + +func TestValidMessage(t *testing.T) { + testString := "requested blacklisted path" + statusCode := http.StatusForbidden + + expected := strings.ReplaceAll( + strings.ReplaceAll(ErrorPage, "%message%", testString), + "%status%", + http.StatusText(statusCode)) + actual := generateResponse(testString, statusCode) + + if expected != actual { + t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual) + } +} + +func TestMessageWithHtml(t *testing.T) { + testString := `abc Date: Wed, 4 Jan 2023 04:51:27 +0000 Subject: [PATCH 178/320] Safely get certificate's leaf (#150) - It's not guaranteed that `tls.X509KeyPair` will set `c.Leaf`. - This patch fixes this by using a wrapper that parses the leaf certificate(in bytes) if `c.Leaf` wasn't set. - Resolves #149 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/150 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/certificates/certificates.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 8af4be5..e3cdbfb 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -303,7 +303,11 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re log.Error().Err(err).Msgf("Couldn't obtain again a certificate or %v", domains) if renew != nil && renew.CertURL != "" { tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) - if err == nil && tlsCertificate.Leaf.NotAfter.After(time.Now()) { + if err != nil { + return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err + } + leaf, err := leaf(&tlsCertificate) + if err == nil && leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) if err := keyDatabase.Put(name, renew); err != nil { @@ -529,3 +533,12 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } } } + +// leaf returns the parsed leaf certificate, either from c.leaf or by parsing +// the corresponding c.Certificate[0]. +func leaf(c *tls.Certificate) (*x509.Certificate, error) { + if c.Leaf != nil { + return c.Leaf, nil + } + return x509.ParseCertificate(c.Certificate[0]) +} From f7fad2a5ae0e4942848604a77199f4f1f8a1c3b4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 4 Jan 2023 06:08:06 +0100 Subject: [PATCH 179/320] Integration Tests use https://codeberg.org/cb_pages_tests --- integration/get_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 81d8488..f13ce8b 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -31,7 +31,7 @@ func TestGetRedirect(t *testing.T) { func TestGetContent(t *testing.T) { log.Println("=== TestGetContent ===") // test get image - resp, err := getTestHTTPSClient().Get("https://magiclike.localhost.mock.directory:4430/images/827679288a.jpg") + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg") assert.NoError(t, err) if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { t.FailNow() @@ -42,7 +42,7 @@ func TestGetContent(t *testing.T) { assert.Len(t, resp.Header.Get("ETag"), 42) // specify branch - resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/") + resp, err = getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/pag/@master/") assert.NoError(t, err) if !assert.NotNil(t, resp) { t.FailNow() @@ -53,7 +53,7 @@ func TestGetContent(t *testing.T) { assert.Len(t, resp.Header.Get("ETag"), 44) // access branch name contains '/' - resp, err = getTestHTTPSClient().Get("https://blumia.localhost.mock.directory:4430/pages-server-integration-tests/@docs~main/") + resp, err = getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/blumia/@docs~main/") assert.NoError(t, err) if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) { t.FailNow() @@ -81,7 +81,7 @@ func TestCustomDomain(t *testing.T) { func TestGetNotFound(t *testing.T) { log.Println("=== TestGetNotFound ===") // test custom not found pages - resp, err := getTestHTTPSClient().Get("https://crystal.localhost.mock.directory:4430/pages-404-demo/blah") + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/pages-404-demo/blah") assert.NoError(t, err) if !assert.NotNil(t, resp) { t.FailNow() @@ -95,7 +95,7 @@ func TestGetNotFound(t *testing.T) { func TestFollowSymlink(t *testing.T) { log.Printf("=== TestFollowSymlink ===\n") - resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/link") + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/tests_for_pages-server/@main/link") assert.NoError(t, err) if !assert.NotNil(t, resp) { t.FailNow() @@ -111,7 +111,7 @@ func TestFollowSymlink(t *testing.T) { func TestLFSSupport(t *testing.T) { log.Printf("=== TestLFSSupport ===\n") - resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/tests_for_pages-server/@main/lfs.txt") + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/tests_for_pages-server/@main/lfs.txt") assert.NoError(t, err) if !assert.NotNil(t, resp) { t.FailNow() From c286b3b1d0a21bc5cc447df7744da9894d857e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Leopoldo=20Sologuren=20Guti=C3=A9rrez?= Date: Wed, 4 Jan 2023 05:26:14 +0000 Subject: [PATCH 180/320] Added TokenBucket to limit the rate of validation failures (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added new TockenBucket named `acmeClientFailLimit` to avoid being banned because of the [Failed validation limit](https://letsencrypt.org/docs/failed-validation-limit/) of Let's Encrypt. The behaviour is similar to the other limiters blocking the `obtainCert` func ensuring rate under limit. Co-authored-by: fsologureng Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/151 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Felipe Leopoldo Sologuren Gutiérrez Co-committed-by: Felipe Leopoldo Sologuren Gutiérrez --- server/certificates/certificates.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index e3cdbfb..1aa90a0 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -163,6 +163,9 @@ var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) // rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) var acmeClientRequestLimit = equalizer.NewTokenBucket(5, 1*time.Second) +// rate limit is 5 / hour https://letsencrypt.org/docs/failed-validation-limit/ +var acmeClientFailLimit = equalizer.NewTokenBucket(5, 1*time.Hour) + type AcmeTLSChallengeProvider struct { challengeCache cache.SetGetKey } @@ -278,6 +281,9 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re res, err = acmeClient.Certificate.Renew(*renew, true, false, "") if err != nil { log.Error().Err(err).Msgf("Couldn't renew certificate for %v, trying to request a new one", domains) + if acmeUseRateLimits { + acmeClientFailLimit.Take() + } res = nil } } @@ -298,6 +304,9 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re Bundle: true, MustStaple: false, }) + if acmeUseRateLimits && err != nil { + acmeClientFailLimit.Take() + } } if err != nil { log.Error().Err(err).Msgf("Couldn't obtain again a certificate or %v", domains) From bd538abd372c41336c355a7d0890a289bad256a5 Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 11 Jan 2023 00:00:37 +0000 Subject: [PATCH 181/320] Fix wrong redirect on custom domain with path (#154) closes #153 Co-authored-by: crapStone Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/154 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- integration/get_test.go | 18 ++++++++++++++++++ server/handler/handler_custom_domain.go | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/integration/get_test.go b/integration/get_test.go index f13ce8b..556ac53 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -78,6 +78,24 @@ func TestCustomDomain(t *testing.T) { assert.EqualValues(t, 106, getSize(resp.Body)) } +func TestCustomDomainRedirects(t *testing.T) { + log.Println("=== TestCustomDomainRedirects ===") + // test redirect from default pages domain to custom domain + resp, err := getTestHTTPSClient().Get("https://6543.localhost.mock.directory:4430/test_pages-server_custom-mock-domain/@main/README.md") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + // TODO: custom port is not evaluated (witch does hurt tests & dev env only) + assert.EqualValues(t, "https://mock-pages.codeberg-test.org/@main/README.md", resp.Header.Get("Location")) + assert.EqualValues(t, `https:/codeberg.org/6543/test_pages-server_custom-mock-domain/src/branch/main/README.md; rel="canonical"; rel="canonical"`, resp.Header.Get("Link")) + + // TODO: test redirect from an custom domain to the primary custom domain (www.example.com -> example.com) + // (cover bug https://codeberg.org/Codeberg/pages-server/issues/153) +} + func TestGetNotFound(t *testing.T) { log.Println("=== TestGetNotFound ===") // test custom not found pages diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 2f98085..a541b74 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -54,7 +54,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g // only redirect if the target is also a codeberg page! targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, dnsLookupCache) if targetOwner != "" { - ctx.Redirect("https://"+canonicalDomain+targetOpt.TargetPath, http.StatusTemporaryRedirect) + ctx.Redirect("https://"+canonicalDomain+"/"+targetOpt.TargetPath, http.StatusTemporaryRedirect) return } From 513e79832ae5bd8e825eb4f5a2605b1c585eccd6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 22 Jan 2023 18:52:21 +0000 Subject: [PATCH 182/320] Use correct log level for `CheckCanonicalDomain` (#162) - Currently any error generated by requesting the `.domains` file of a repository would be logged under the info log level, which isn't the correct log level when we exclude the not found error. - Use warn log level if the error isn't the not found error. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/162 Reviewed-by: Otto --- server/upstream/domains.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 0e29673..ba1c494 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -45,7 +45,11 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, } } } else { - log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + if err != gitea.ErrorNotFound { + log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + } else { + log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + } } domains = append(domains, o.TargetOwner+mainDomainSuffix) if domains[len(domains)-1] == actualDomain { From 2c2087953d975c8cc9b57f6b7c94cb18a7894170 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 9 Feb 2023 17:30:06 +0000 Subject: [PATCH 183/320] Add Integration test for custom domain redirect to another custom domain (#172) address #155 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/172 --- integration/get_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 556ac53..6dd57bc 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -89,11 +89,22 @@ func TestCustomDomainRedirects(t *testing.T) { assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) // TODO: custom port is not evaluated (witch does hurt tests & dev env only) + // assert.EqualValues(t, "https://mock-pages.codeberg-test.org:4430/@main/README.md", resp.Header.Get("Location")) assert.EqualValues(t, "https://mock-pages.codeberg-test.org/@main/README.md", resp.Header.Get("Location")) assert.EqualValues(t, `https:/codeberg.org/6543/test_pages-server_custom-mock-domain/src/branch/main/README.md; rel="canonical"; rel="canonical"`, resp.Header.Get("Link")) - // TODO: test redirect from an custom domain to the primary custom domain (www.example.com -> example.com) - // (cover bug https://codeberg.org/Codeberg/pages-server/issues/153) + // test redirect from an custom domain to the primary custom domain (www.example.com -> example.com) + // regression test to https://codeberg.org/Codeberg/pages-server/issues/153 + resp, err = getTestHTTPSClient().Get("https://mock-pages-redirect.codeberg-test.org:4430/README.md") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + // TODO: custom port is not evaluated (witch does hurt tests & dev env only) + // assert.EqualValues(t, "https://mock-pages.codeberg-test.org:4430/README.md", resp.Header.Get("Location")) + assert.EqualValues(t, "https://mock-pages.codeberg-test.org/README.md", resp.Header.Get("Location")) } func TestGetNotFound(t *testing.T) { From 8b1f497bc4699a76c8323a1466a93de4fc0f8b4d Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 10 Feb 2023 01:38:15 +0000 Subject: [PATCH 184/320] Allow to use certificate even if domain validation fails (#160) - Currently if the canonical domain validations fails(either for legitimate reasons or for bug reasons like the request to Gitea/Forgejo failing) it will use main domain certificate, which in the case for custom domains will warrant a security error as the certificate isn't issued to the custom domain. - This patch handles this situation more gracefully and instead only disallow obtaining a certificate if the domain validation fails, so in the case that a certificate still exists it can still be used even if the canonical domain validation fails. There's a small side effect, legitimate users that remove domains from `.domain` will still be able to use the removed domain(as long as the DNS records exists) as long as the certificate currently hold by pages-server isn't expired. - Given the increased usage in custom domains that are resulting in errors, I think it ways more than the side effect. - In order to future-proof against future slowdowns of instances, add a retry mechanism to the domain validation function, such that it's more likely to succeed even if the instance is not responding. - Refactor the code a bit and add some comments. Co-authored-by: Gusted Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/160 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/certificates/certificates.go | 9 +++- server/upstream/domains.go | 71 +++++++++++++++-------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 1aa90a0..0bf5672 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -70,6 +70,7 @@ func TLSConfig(mainDomainSuffix string, } targetOwner := "" + mayObtainCert := true if strings.HasSuffix(sni, mainDomainSuffix) || strings.EqualFold(sni, mainDomainSuffix[1:]) { // deliver default certificate for the main domain (*.codeberg.page) sni = mainDomainSuffix @@ -87,7 +88,9 @@ func TLSConfig(mainDomainSuffix string, } _, valid := targetOpt.CheckCanonicalDomain(giteaClient, sni, mainDomainSuffix, canonicalDomainCache) if !valid { - sni = mainDomainSuffix + // We shouldn't obtain a certificate when we cannot check if the + // repository has specified this domain in the `.domains` file. + mayObtainCert = false } } } @@ -106,6 +109,10 @@ func TLSConfig(mainDomainSuffix string, return nil, errors.New("won't request certificate for main domain, something really bad has happened") } + if !mayObtainCert { + return nil, fmt.Errorf("won't request certificate for %q", sni) + } + tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { return nil, err diff --git a/server/upstream/domains.go b/server/upstream/domains.go index ba1c494..5b274b6 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -16,49 +16,54 @@ var canonicalDomainCacheTimeout = 15 * time.Minute const canonicalDomainConfig = ".domains" // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). -func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (string, bool) { - var ( - domains []string - valid bool - ) +func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (domain string, valid bool) { + // Check if this request is cached. if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { - domains = cachedValue.([]string) + domains := cachedValue.([]string) for _, domain := range domains { if domain == actualDomain { valid = true break } } - } else { - body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) - if err == nil { - for _, domain := range strings.Split(string(body), "\n") { - domain = strings.ToLower(domain) - domain = strings.TrimSpace(domain) - domain = strings.TrimPrefix(domain, "http://") - domain = strings.TrimPrefix(domain, "https://") - if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { - domains = append(domains, domain) - } - if domain == actualDomain { - valid = true - } - } - } else { - if err != gitea.ErrorNotFound { - log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) - } else { - log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) - } + return domains[0], valid + } + + body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) + if err == nil || err == gitea.ErrorNotFound { + log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + } + + var domains []string + for _, domain := range strings.Split(string(body), "\n") { + domain = strings.ToLower(domain) + domain = strings.TrimSpace(domain) + domain = strings.TrimPrefix(domain, "http://") + domain = strings.TrimPrefix(domain, "https://") + if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { + domains = append(domains, domain) } - domains = append(domains, o.TargetOwner+mainDomainSuffix) - if domains[len(domains)-1] == actualDomain { + if domain == actualDomain { valid = true } - if o.TargetRepo != "" && o.TargetRepo != "pages" { - domains[len(domains)-1] += "/" + o.TargetRepo - } - _ = canonicalDomainCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, domains, canonicalDomainCacheTimeout) } + + // Add [owner].[pages-domain] as valid domnain. + domains = append(domains, o.TargetOwner+mainDomainSuffix) + if domains[len(domains)-1] == actualDomain { + valid = true + } + + // If the target repository isn't called pages, add `/[repository]` to the + // previous valid domain. + if o.TargetRepo != "" && o.TargetRepo != "pages" { + domains[len(domains)-1] += "/" + o.TargetRepo + } + + // Add result to cache. + _ = canonicalDomainCache.Set(o.TargetOwner+"/"+o.TargetRepo+"/"+o.TargetBranch, domains, canonicalDomainCacheTimeout) + + // Return the first domain from the list and return if any of the domains + // matched the requested domain. return domains[0], valid } From 7fce7cf68bcc35cd5d054b0434d6943d77f7053a Mon Sep 17 00:00:00 2001 From: foehammer Date: Fri, 10 Feb 2023 01:44:44 +0000 Subject: [PATCH 185/320] Added Whitespace Trimming TXT DNS Records (#152) Solves https://codeberg.org/Codeberg/Community/issues/823 and https://codeberg.org/Codeberg/pages-server/issues/143 Co-authored-by: foehammer127 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/152 Reviewed-by: Otto Reviewed-by: Gusted Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: foehammer Co-committed-by: foehammer --- server/dns/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/dns/dns.go b/server/dns/dns.go index 818e29a..2719d4d 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -28,7 +28,7 @@ func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetG names, err := net.LookupTXT(domain) if err == nil { for _, name := range names { - name = strings.TrimSuffix(name, ".") + name = strings.TrimSuffix(strings.TrimSpace(name), ".") if strings.HasSuffix(name, mainDomainSuffix) { cname = name break From 7b35a192bf4f87ca93c7d3a8a3d3dd2672c22fec Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 10 Feb 2023 03:00:14 +0000 Subject: [PATCH 186/320] Add cert store option based on sqlite3, mysql & postgres (#173) Deprecate **pogreb**! close #169 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/173 --- .gitignore | 2 + .golangci.yml | 14 + .woodpecker.yml | 15 +- Dockerfile | 5 +- Justfile | 20 +- README.md | 3 +- cmd/certs.go | 97 ++++-- cmd/flags.go | 259 ++++++++-------- cmd/main.go | 17 +- cmd/setup.go | 45 +++ go.mod | 18 +- go.sum | 377 +++++++++++++++++++++++- integration/main_test.go | 3 +- main.go | 2 +- server/certificates/certificates.go | 133 +++++---- server/certificates/mock.go | 15 +- server/certificates/mock_test.go | 3 +- server/database/interface.go | 62 +++- server/database/mock.go | 5 +- server/database/{setup.go => pogreb.go} | 31 +- server/database/xorm.go | 121 ++++++++ server/upstream/helper.go | 8 +- 22 files changed, 1000 insertions(+), 255 deletions(-) create mode 100644 cmd/setup.go rename server/database/{setup.go => pogreb.go} (76%) create mode 100644 server/database/xorm.go diff --git a/.gitignore b/.gitignore index dfe69ac..8745935 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ .idea/ +.cache/ *.iml key-database.pogreb/ acme-account.json build/ vendor/ pages +certs.sqlite diff --git a/.golangci.yml b/.golangci.yml index 6d9b95a..488ca09 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,9 +12,23 @@ linters-settings: - hugeParam linters: + disable-all: true enable: - unconvert - gocritic + - gofumpt + - bidichk + - errcheck + - gofmt + - goimports + - gosimple + - govet + - ineffassign + - misspell + - staticcheck + - typecheck + - unused + - whitespace run: timeout: 5m diff --git a/.woodpecker.yml b/.woodpecker.yml index 7e6d694..ba44f82 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,7 +1,7 @@ pipeline: # use vendor to cache dependencies vendor: - image: golang:1.18 + image: golang:1.20 commands: - go mod vendor @@ -65,6 +65,19 @@ pipeline: - RAW_DOMAIN=raw.localhost.mock.directory - PORT=4430 + # TODO: remove in next version + integration-tests-legacy: + group: test + image: codeberg.org/6543/docker-images/golang_just + commands: + - just integration + environment: + - ACME_API=https://acme.mock.directory + - PAGES_DOMAIN=localhost.mock.directory + - RAW_DOMAIN=raw.localhost.mock.directory + - PORT=4430 + - DB_TYPE= + release: image: plugins/gitea-release settings: diff --git a/Dockerfile b/Dockerfile index 904d6f4..eec97de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,9 @@ -FROM golang:alpine as build +FROM techknowlogick/xgo as build WORKDIR /workspace -RUN apk add ca-certificates COPY . . -RUN CGO_ENABLED=0 go build . +RUN CGO_ENABLED=1 go build -tags 'sqlite sqlite_unlock_notify netgo' -ldflags '-s -w -extldflags "-static" -linkmode external' . FROM scratch COPY --from=build /workspace/pages /pages diff --git a/Justfile b/Justfile index 7d72fe8..683222d 100644 --- a/Justfile +++ b/Justfile @@ -1,3 +1,6 @@ +CGO_FLAGS := '-extldflags "-static" -linkmode external' +TAGS := 'sqlite sqlite_unlock_notify netgo' + dev: #!/usr/bin/env bash set -euxo pipefail @@ -7,16 +10,15 @@ dev: export RAW_DOMAIN=raw.localhost.mock.directory export PORT=4430 export LOG_LEVEL=trace - go run . + go run -tags '{{TAGS}}' . build: - CGO_ENABLED=0 go build -ldflags '-s -w' -v -o build/codeberg-pages-server ./ + CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./ build-tag VERSION: - CGO_ENABLED=0 go build -ldflags '-s -w -X "codeberg.org/codeberg/pages/server/version.Version={{VERSION}}"' -v -o build/codeberg-pages-server ./ + CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w -X "codeberg.org/codeberg/pages/server/version.Version={{VERSION}}" {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./ lint: tool-golangci tool-gofumpt - [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \ golangci-lint run --timeout 5m --build-tags integration # TODO: run editorconfig-checker @@ -25,7 +27,7 @@ fmt: tool-gofumpt clean: go clean ./... - rm -rf build/ + rm -rf build/ integration/certs.sqlite integration/key-database.pogreb/ integration/acme-account.json tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ @@ -38,13 +40,13 @@ tool-gofumpt: fi test: - go test -race codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ + go test -race -cover -tags '{{TAGS}}' codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ test-run TEST: - go test -race -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ + go test -race -tags '{{TAGS}}' -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ integration: - go test -race -tags integration codeberg.org/codeberg/pages/integration/... + go test -race -tags 'integration {{TAGS}}' codeberg.org/codeberg/pages/integration/... integration-run TEST: - go test -race -tags integration -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... + go test -race -tags 'integration {{TAGS}}' -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... diff --git a/README.md b/README.md index 50cdc3c..6d5a294 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ Thank you very much. run `just dev` now this pages should work: - - https://magiclike.localhost.mock.directory:4430/ + - https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg - https://momar.localhost.mock.directory:4430/ci-testing/ - https://momar.localhost.mock.directory:4430/pag/@master/ + - https://mock-pages.codeberg-test.org:4430/README.md diff --git a/cmd/certs.go b/cmd/certs.go index d93fe13..4adf076 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -2,8 +2,10 @@ package cmd import ( "fmt" + "time" - "github.com/akrylysov/pogreb" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" "codeberg.org/codeberg/pages/server/database" @@ -23,25 +25,85 @@ var Certs = &cli.Command{ Usage: "remove a certificate from the database", Action: removeCert, }, + { + Name: "migrate", + Usage: "migrate from \"pogreb\" driver to dbms driver", + Action: migrateCerts, + }, }, + Flags: append(CertStorageFlags, []cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + Usage: "print trace info", + EnvVars: []string{"VERBOSE"}, + Value: false, + }, + }...), +} + +func migrateCerts(ctx *cli.Context) error { + dbType := ctx.String("db-type") + if dbType == "" { + dbType = "sqlite3" + } + dbConn := ctx.String("db-conn") + dbPogrebConn := ctx.String("db-pogreb") + verbose := ctx.Bool("verbose") + + log.Level(zerolog.InfoLevel) + if verbose { + log.Level(zerolog.TraceLevel) + } + + xormDB, err := database.NewXormDB(dbType, dbConn) + if err != nil { + return fmt.Errorf("could not connect to database: %w", err) + } + defer xormDB.Close() + + pogrebDB, err := database.NewPogreb(dbPogrebConn) + if err != nil { + return fmt.Errorf("could not open database: %w", err) + } + defer pogrebDB.Close() + + fmt.Printf("Start migration from \"%s\" to \"%s:%s\" ...\n", dbPogrebConn, dbType, dbConn) + + certs, err := pogrebDB.Items(0, 0) + if err != nil { + return err + } + + for _, cert := range certs { + if err := xormDB.Put(cert.Domain, cert.Raw()); err != nil { + return err + } + } + + fmt.Println("... done") + return nil } func listCerts(ctx *cli.Context) error { - // TODO: make "key-database.pogreb" set via flag - keyDatabase, err := database.New("key-database.pogreb") + certDB, closeFn, err := openCertDB(ctx) if err != nil { - return fmt.Errorf("could not create database: %v", err) + return err + } + defer closeFn() + + items, err := certDB.Items(0, 0) + if err != nil { + return err } - items := keyDatabase.Items() - for domain, _, err := items.Next(); err != pogreb.ErrIterationDone; domain, _, err = items.Next() { - if err != nil { - return err + fmt.Printf("Domain\tValidTill\n\n") + for _, cert := range items { + if cert.Domain[0] == '.' { + cert.Domain = "*" + cert.Domain } - if domain[0] == '.' { - fmt.Printf("*") - } - fmt.Printf("%s\n", domain) + fmt.Printf("%s\t%s\n", + cert.Domain, + time.Unix(cert.ValidTill, 0).Format(time.RFC3339)) } return nil } @@ -53,20 +115,17 @@ func removeCert(ctx *cli.Context) error { domains := ctx.Args().Slice() - // TODO: make "key-database.pogreb" set via flag - keyDatabase, err := database.New("key-database.pogreb") + certDB, closeFn, err := openCertDB(ctx) if err != nil { - return fmt.Errorf("could not create database: %v", err) + return err } + defer closeFn() for _, domain := range domains { fmt.Printf("Removing domain %s from the database...\n", domain) - if err := keyDatabase.Delete(domain); err != nil { + if err := certDB.Delete(domain); err != nil { return err } } - if err := keyDatabase.Close(); err != nil { - return err - } return nil } diff --git a/cmd/flags.go b/cmd/flags.go index 8ac09ec..40bb053 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -4,120 +4,149 @@ import ( "github.com/urfave/cli/v2" ) -var ServeFlags = []cli.Flag{ - // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static - // pages, or used for comparison in CNAME lookups. Static pages can be accessed through - // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". - &cli.StringFlag{ - Name: "pages-domain", - Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", - EnvVars: []string{"PAGES_DOMAIN"}, - Value: "codeberg.page", - }, - // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. - &cli.StringFlag{ - Name: "gitea-root", - Usage: "specifies the root URL of the Gitea instance, without a trailing slash.", - EnvVars: []string{"GITEA_ROOT"}, - Value: "https://codeberg.org", - }, - // GiteaApiToken specifies an api token for the Gitea instance - &cli.StringFlag{ - Name: "gitea-api-token", - Usage: "specifies an api token for the Gitea instance", - EnvVars: []string{"GITEA_API_TOKEN"}, - Value: "", - }, - // RawDomain specifies the domain from which raw repository content shall be served in the following format: - // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} - // (set to []byte(nil) to disable raw content hosting) - &cli.StringFlag{ - Name: "raw-domain", - Usage: "specifies the domain from which raw repository content shall be served, not set disable raw content hosting", - EnvVars: []string{"RAW_DOMAIN"}, - Value: "raw.codeberg.page", - }, - // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). - &cli.StringFlag{ - Name: "raw-info-page", - Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", - EnvVars: []string{"RAW_INFO_PAGE"}, - Value: "https://docs.codeberg.org/codeberg-pages/raw-content/", - }, +var ( + CertStorageFlags = []cli.Flag{ + &cli.StringFlag{ + // TODO: remove in next version + // DEPRICATED + Name: "db-pogreb", + Value: "key-database.pogreb", + EnvVars: []string{"DB_POGREB"}, + }, + &cli.StringFlag{ + Name: "db-type", + Value: "", // TODO: "sqlite3" in next version + EnvVars: []string{"DB_TYPE"}, + }, + &cli.StringFlag{ + Name: "db-conn", + Value: "certs.sqlite", + EnvVars: []string{"DB_CONN"}, + }, + } - // Server - &cli.StringFlag{ - Name: "host", - Usage: "specifies host of listening address", - EnvVars: []string{"HOST"}, - Value: "[::]", - }, - &cli.StringFlag{ - Name: "port", - Usage: "specifies port of listening address", - EnvVars: []string{"PORT"}, - Value: "443", - }, - &cli.BoolFlag{ - Name: "enable-http-server", - // TODO: desc - EnvVars: []string{"ENABLE_HTTP_SERVER"}, - }, - // Server Options - &cli.BoolFlag{ - Name: "enable-lfs-support", - Usage: "enable lfs support, require gitea v1.17.0 as backend", - EnvVars: []string{"ENABLE_LFS_SUPPORT"}, - Value: true, - }, - &cli.BoolFlag{ - Name: "enable-symlink-support", - Usage: "follow symlinks if enabled, require gitea v1.18.0 as backend", - EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, - Value: true, - }, - &cli.StringFlag{ - Name: "log-level", - Value: "warn", - Usage: "specify at which log level should be logged. Possible options: info, warn, error, fatal", - EnvVars: []string{"LOG_LEVEL"}, - }, + ServerFlags = append(CertStorageFlags, []cli.Flag{ + // ############# + // ### Gitea ### + // ############# + // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. + &cli.StringFlag{ + Name: "gitea-root", + Usage: "specifies the root URL of the Gitea instance, without a trailing slash.", + EnvVars: []string{"GITEA_ROOT"}, + Value: "https://codeberg.org", + }, + // GiteaApiToken specifies an api token for the Gitea instance + &cli.StringFlag{ + Name: "gitea-api-token", + Usage: "specifies an api token for the Gitea instance", + EnvVars: []string{"GITEA_API_TOKEN"}, + Value: "", + }, - // ACME - &cli.StringFlag{ - Name: "acme-api-endpoint", - EnvVars: []string{"ACME_API"}, - Value: "https://acme-v02.api.letsencrypt.org/directory", - }, - &cli.StringFlag{ - Name: "acme-email", - EnvVars: []string{"ACME_EMAIL"}, - Value: "noreply@example.email", - }, - &cli.BoolFlag{ - Name: "acme-use-rate-limits", - // TODO: Usage - EnvVars: []string{"ACME_USE_RATE_LIMITS"}, - Value: true, - }, - &cli.BoolFlag{ - Name: "acme-accept-terms", - // TODO: Usage - EnvVars: []string{"ACME_ACCEPT_TERMS"}, - }, - &cli.StringFlag{ - Name: "acme-eab-kid", - // TODO: Usage - EnvVars: []string{"ACME_EAB_KID"}, - }, - &cli.StringFlag{ - Name: "acme-eab-hmac", - // TODO: Usage - EnvVars: []string{"ACME_EAB_HMAC"}, - }, - &cli.StringFlag{ - Name: "dns-provider", - // TODO: Usage - EnvVars: []string{"DNS_PROVIDER"}, - }, -} + // ########################### + // ### Page Server Domains ### + // ########################### + // MainDomainSuffix specifies the main domain (starting with a dot) for which subdomains shall be served as static + // pages, or used for comparison in CNAME lookups. Static pages can be accessed through + // https://{owner}.{MainDomain}[/{repo}], with repo defaulting to "pages". + &cli.StringFlag{ + Name: "pages-domain", + Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", + EnvVars: []string{"PAGES_DOMAIN"}, + Value: "codeberg.page", + }, + // RawDomain specifies the domain from which raw repository content shall be served in the following format: + // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} + // (set to []byte(nil) to disable raw content hosting) + &cli.StringFlag{ + Name: "raw-domain", + Usage: "specifies the domain from which raw repository content shall be served, not set disable raw content hosting", + EnvVars: []string{"RAW_DOMAIN"}, + Value: "raw.codeberg.page", + }, + // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). + &cli.StringFlag{ + Name: "raw-info-page", + Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", + EnvVars: []string{"RAW_INFO_PAGE"}, + Value: "https://docs.codeberg.org/codeberg-pages/raw-content/", + }, + + // Server + &cli.StringFlag{ + Name: "host", + Usage: "specifies host of listening address", + EnvVars: []string{"HOST"}, + Value: "[::]", + }, + &cli.StringFlag{ + Name: "port", + Usage: "specifies port of listening address", + EnvVars: []string{"PORT"}, + Value: "443", + }, + &cli.BoolFlag{ + Name: "enable-http-server", + // TODO: desc + EnvVars: []string{"ENABLE_HTTP_SERVER"}, + }, + // Server Options + &cli.BoolFlag{ + Name: "enable-lfs-support", + Usage: "enable lfs support, require gitea v1.17.0 as backend", + EnvVars: []string{"ENABLE_LFS_SUPPORT"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "enable-symlink-support", + Usage: "follow symlinks if enabled, require gitea v1.18.0 as backend", + EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, + Value: true, + }, + &cli.StringFlag{ + Name: "log-level", + Value: "warn", + Usage: "specify at which log level should be logged. Possible options: info, warn, error, fatal", + EnvVars: []string{"LOG_LEVEL"}, + }, + + // ACME + &cli.StringFlag{ + Name: "acme-api-endpoint", + EnvVars: []string{"ACME_API"}, + Value: "https://acme-v02.api.letsencrypt.org/directory", + }, + &cli.StringFlag{ + Name: "acme-email", + EnvVars: []string{"ACME_EMAIL"}, + Value: "noreply@example.email", + }, + &cli.BoolFlag{ + Name: "acme-use-rate-limits", + // TODO: Usage + EnvVars: []string{"ACME_USE_RATE_LIMITS"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "acme-accept-terms", + // TODO: Usage + EnvVars: []string{"ACME_ACCEPT_TERMS"}, + }, + &cli.StringFlag{ + Name: "acme-eab-kid", + // TODO: Usage + EnvVars: []string{"ACME_EAB_KID"}, + }, + &cli.StringFlag{ + Name: "acme-eab-hmac", + // TODO: Usage + EnvVars: []string{"ACME_EAB_HMAC"}, + }, + &cli.StringFlag{ + Name: "dns-provider", + // TODO: Usage + EnvVars: []string{"DNS_PROVIDER"}, + }, + }...) +) diff --git a/cmd/main.go b/cmd/main.go index b72013a..af4d2ce 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,7 +18,6 @@ import ( "codeberg.org/codeberg/pages/server" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/certificates" - "codeberg.org/codeberg/pages/server/database" "codeberg.org/codeberg/pages/server/gitea" "codeberg.org/codeberg/pages/server/handler" ) @@ -38,7 +37,7 @@ var BlacklistedPaths = []string{ // Serve sets up and starts the web server. func Serve(ctx *cli.Context) error { - // Initalize the logger. + // Initialize the logger. logLevel, err := zerolog.ParseLevel(ctx.String("log-level")) if err != nil { return err @@ -74,6 +73,13 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix = "." + mainDomainSuffix } + // Init ssl cert database + certDB, closeFn, err := openCertDB(ctx) + if err != nil { + return err + } + defer closeFn() + keyCache := cache.NewKeyValueCache() challengeCache := cache.NewKeyValueCache() // canonicalDomainCache stores canonical domains @@ -104,13 +110,6 @@ func Serve(ctx *cli.Context) error { return fmt.Errorf("couldn't create listener: %v", err) } - // TODO: make "key-database.pogreb" set via flag - certDB, err := database.New("key-database.pogreb") - if err != nil { - return fmt.Errorf("could not create database: %v", err) - } - defer certDB.Close() //nolint:errcheck // database has no close ... sync behave like it - listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaClient, dnsProvider, diff --git a/cmd/setup.go b/cmd/setup.go new file mode 100644 index 0000000..3d1d6ee --- /dev/null +++ b/cmd/setup.go @@ -0,0 +1,45 @@ +package cmd + +import ( + "fmt" + + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" + + "codeberg.org/codeberg/pages/server/database" +) + +func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) { + if ctx.String("db-type") != "" { + log.Trace().Msg("use xorm mode") + certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) + if err != nil { + return nil, nil, fmt.Errorf("could not connect to database: %w", err) + } + } else { + // TODO: remove in next version + fmt.Println(` +###################### +## W A R N I N G !!! # +###################### + +You use "pogreb" witch is deprecated and will be removed in the next version. +Please switch to sqlite, mysql or postgres !!! + +The simplest way is, to use './pages certs migrate' and set environment var DB_TYPE to 'sqlite' on next start.`) + log.Error().Msg("depricated \"pogreb\" used\n") + + certDB, err = database.NewPogreb(ctx.String("db-pogreb")) + if err != nil { + return nil, nil, fmt.Errorf("could not create database: %w", err) + } + } + + closeFn = func() { + if err := certDB.Close(); err != nil { + log.Error().Err(err) + } + } + + return certDB, closeFn, nil +} diff --git a/go.mod b/go.mod index 77ed762..2e75c8e 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,21 @@ module codeberg.org/codeberg/pages -go 1.19 +go 1.20 require ( code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 + github.com/go-sql-driver/mysql v1.6.0 github.com/joho/godotenv v1.4.0 + github.com/lib/pq v1.10.7 + github.com/mattn/go-sqlite3 v1.14.16 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 + xorm.io/xorm v1.3.2 ) require ( @@ -47,11 +51,13 @@ require ( github.com/go-errors/errors v1.0.1 // indirect github.com/go-fed/httpsig v1.1.0 // indirect github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect - github.com/gofrs/uuid v3.2.0+incompatible // indirect + github.com/goccy/go-json v0.8.1 // indirect + github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.1.1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/gophercloud/gophercloud v0.16.0 // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect @@ -62,7 +68,7 @@ require ( github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/jarcoal/httpmock v1.0.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/json-iterator/go v1.1.7 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect @@ -78,7 +84,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect github.com/nrdcg/auroradns v1.0.1 // indirect github.com/nrdcg/desec v0.6.0 // indirect @@ -103,6 +109,7 @@ require ( github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/spf13/cast v1.3.1 // indirect github.com/stretchr/objx v0.3.0 // indirect + github.com/syndtr/goleveldb v1.0.0 // indirect github.com/transip/gotransip/v6 v6.6.1 // indirect github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect github.com/vultr/govultr/v2 v2.7.1 // indirect @@ -124,4 +131,5 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + xorm.io/builder v0.3.12 // indirect ) diff --git a/go.sum b/go.sum index a44001c..c1042c9 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,9 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa h1:OVwgYrY6vr6gWZvgnmevFhtL0GVA4HKaFOhD+joPoNk= code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= +gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -55,24 +58,38 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbxuNdz3FeyvHFnRjmo= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -81,27 +98,39 @@ github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJm github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.20.0 h1:y2a6KwYHTFxhw+8PLhz0q5hpTGj6Un3W1pbpQLhzFaE= github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -110,6 +139,7 @@ github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454Wv github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpAMecnw= github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= +github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= @@ -118,6 +148,13 @@ github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnsimple/dnsimple-go v0.70.1 h1:cSZndVjttLpgplDuesY4LFIvfKf/zRA1J7mCATBbzSM= github.com/dnsimple/dnsimple-go v0.70.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/exoscale/egoscale v0.67.0 h1:qgWh7T5IZGrNWtg6ib4dr+76WThvB+odTtGG+DGbXF8= @@ -127,6 +164,8 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -144,24 +183,37 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= +github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= +github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -186,6 +238,10 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -194,6 +250,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= @@ -208,8 +265,10 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -220,16 +279,23 @@ github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15 github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -246,6 +312,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -258,12 +325,61 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= +github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= +github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g= github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= @@ -277,8 +393,10 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -286,6 +404,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= @@ -298,6 +418,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk= @@ -306,6 +427,15 @@ github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linode/linodego v0.31.1 h1:dBtjKo7J9UhNFhTOclEXb12RRyQDaRBxISdONVuU+DA= github.com/linode/linodego v0.31.1/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= @@ -315,19 +445,25 @@ github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAW github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= github.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOEYGSKrk4= github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -337,6 +473,9 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -360,11 +499,19 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y= github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= @@ -383,29 +530,49 @@ github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -414,27 +581,41 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= @@ -442,12 +623,18 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= @@ -466,21 +653,28 @@ github.com/softlayer/softlayer-go v1.0.3/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qy github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -490,11 +684,16 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggsC4564= github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= @@ -511,7 +710,13 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -519,23 +724,37 @@ go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 h1:d9qaMM+ODpCq+9We41//fu/sHsTnXcrqd1en3x+GKy4= go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -570,6 +789,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -579,6 +799,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -589,6 +810,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -629,9 +851,11 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -641,6 +865,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -648,6 +873,7 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -663,13 +889,16 @@ golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -684,6 +913,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -692,6 +922,7 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -700,14 +931,18 @@ golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -716,6 +951,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -725,12 +961,17 @@ golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -743,6 +984,7 @@ google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -753,6 +995,7 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -767,10 +1010,15 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= @@ -789,10 +1037,13 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -806,6 +1057,7 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -818,12 +1070,129 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U= +modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= +modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= +modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= +modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= +modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= +modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= +modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= +modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= +modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= +modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= +modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= +modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= +modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= +modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= +modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= +modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= +modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= +modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= +modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= +modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= +modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= +modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= +modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= +modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= +modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= +modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= +modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= +modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= +modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= +modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= +modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= +modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= +modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= +modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8= +modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= +modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= +modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= +modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= +modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= +modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= +modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= +modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= +modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= +modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= +modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= +modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= +modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= +modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= +modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= +modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= +modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= +modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= +modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= +modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= +modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= +modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= +modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= +modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= +modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= +modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= +modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= +modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= +modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= +modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= +modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= +modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= +modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE= +modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= +modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= +modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE= +modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= +modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= +modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM= +xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/xorm v1.3.2 h1:uTRRKF2jYzbZ5nsofXVUx6ncMaek+SHjWYtCXyZo1oM= +xorm.io/xorm v1.3.2/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= diff --git a/integration/main_test.go b/integration/main_test.go index 406b33a..3e0e187 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -40,11 +40,12 @@ func startServer(ctx context.Context) error { setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") setEnvIfNotSet("PORT", "4430") + setEnvIfNotSet("DB_TYPE", "sqlite3") app := cli.NewApp() app.Name = "pages-server" app.Action = cmd.Serve - app.Flags = cmd.ServeFlags + app.Flags = cmd.ServerFlags go func() { if err := app.RunContext(ctx, args); err != nil { diff --git a/main.go b/main.go index 2836b86..8eac1fb 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ func main() { app.Version = version app.Usage = "pages server" app.Action = cmd.Serve - app.Flags = cmd.ServeFlags + app.Flags = cmd.ServerFlags app.Commands = []*cli.Command{ cmd.Certs, } diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 0bf5672..555539e 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -1,14 +1,12 @@ package certificates import ( - "bytes" "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" - "encoding/gob" "encoding/json" "errors" "fmt" @@ -100,10 +98,9 @@ func TLSConfig(mainDomainSuffix string, return tlsCertificate.(*tls.Certificate), nil } - var tlsCertificate tls.Certificate + var tlsCertificate *tls.Certificate var err error - var ok bool - if tlsCertificate, ok = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); !ok { + if tlsCertificate, err = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); err != nil { // request a new certificate if strings.EqualFold(sni, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") @@ -119,12 +116,11 @@ func TLSConfig(mainDomainSuffix string, } } - if err := keyCache.Set(sni, &tlsCertificate, 15*time.Minute); err != nil { + if err := keyCache.Set(sni, tlsCertificate, 15*time.Minute); err != nil { return nil, err } - return &tlsCertificate, nil + return tlsCertificate, nil }, - PreferServerCipherSuites: true, NextProtos: []string{ "h2", "http/1.1", @@ -205,54 +201,53 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (tls.Certificate, bool) { +func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (*tls.Certificate, error) { // parse certificate from database res, err := certDB.Get(sni) if err != nil { - panic(err) // TODO: no panic - } - if res == nil { - return tls.Certificate{}, false + return nil, err + } else if res == nil { + return nil, database.ErrNotFound } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { - panic(err) + return nil, err } // TODO: document & put into own function if !strings.EqualFold(sni, mainDomainSuffix) { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { - panic(err) + return nil, fmt.Errorf("error parsin leaf tlsCert: %w", err) } // renew certificates 7 days before they expire if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { - // TODO: add ValidUntil to custom res struct + // TODO: use ValidTill of custom cert struct if res.CSR != nil && len(res.CSR) > 0 { // CSR stores the time when the renewal shall be tried again nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64) if err == nil && time.Now().Before(time.Unix(nextTryUnix, 0)) { - return tlsCertificate, true + return &tlsCertificate, nil } } + // TODO: make a queue ? go (func() { res.CSR = nil // acme client doesn't like CSR to be set - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) - if err != nil { + if _, err := obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB); err != nil { log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) } })() } } - return tlsCertificate, true + return &tlsCertificate, nil } var obtainLocks = sync.Map{} -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (tls.Certificate, error) { +func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (*tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] @@ -265,16 +260,16 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re time.Sleep(100 * time.Millisecond) _, working = obtainLocks.Load(name) } - cert, ok := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) - if !ok { - return tls.Certificate{}, errors.New("certificate failed in synchronous request") + cert, err := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + if err != nil { + return nil, fmt.Errorf("certificate failed in synchronous request: %w", err) } return cert, nil } defer obtainLocks.Delete(name) if acmeClient == nil { - return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase), nil + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase) } // request actual cert @@ -297,7 +292,7 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if res == nil { if user != "" { if err := checkUserLimit(user); err != nil { - return tls.Certificate{}, err + return nil, err } } @@ -320,33 +315,42 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re if renew != nil && renew.CertURL != "" { tlsCertificate, err := tls.X509KeyPair(renew.Certificate, renew.PrivateKey) if err != nil { - return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err + mockC, err2 := mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase) + if err2 != nil { + return nil, errors.Join(err, err2) + } + return mockC, err } leaf, err := leaf(&tlsCertificate) if err == nil && leaf.NotAfter.After(time.Now()) { // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) if err := keyDatabase.Put(name, renew); err != nil { - return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err + mockC, err2 := mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase) + if err2 != nil { + return nil, errors.Join(err, err2) + } + return mockC, err } - return tlsCertificate, nil + return &tlsCertificate, nil } } - return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase), err + return mockCert(domains[0], err.Error(), mainDomainSuffix, keyDatabase) } log.Debug().Msgf("Obtained certificate for %v", domains) if err := keyDatabase.Put(name, res); err != nil { - return tls.Certificate{}, err + return nil, err } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { - return tls.Certificate{}, err + return nil, err } - return tlsCertificate, nil + return &tlsCertificate, nil } func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { + // TODO: make it a config flag const configFile = "acme-account.json" var myAcmeAccount AcmeAccount var myAcmeConfig *lego.Config @@ -431,8 +435,8 @@ func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcce func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits mainCertBytes, err := certDB.Get(mainDomainSuffix) - if err != nil { - return fmt.Errorf("cert database is not working") + if err != nil && !errors.Is(err, database.ErrNotFound) { + return fmt.Errorf("cert database is not working: %w", err) } acmeClient, err = lego.NewClient(acmeConfig) @@ -485,41 +489,35 @@ func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Co func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { for { - // clean up expired certs - now := time.Now() + // delete expired certs that will be invalid until next clean up + threshold := time.Now().Add(interval) expiredCertCount := 0 - keyDatabaseIterator := certDB.Items() - key, resBytes, err := keyDatabaseIterator.Next() - for err == nil { - if !strings.EqualFold(string(key), mainDomainSuffix) { - resGob := bytes.NewBuffer(resBytes) - resDec := gob.NewDecoder(resGob) - res := &certificate.Resource{} - err = resDec.Decode(res) - if err != nil { - panic(err) - } - tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - if err != nil || tlsCertificates[0].NotAfter.Before(now) { - err := certDB.Delete(string(key)) - if err != nil { - log.Error().Err(err).Msgf("Deleting expired certificate for %q failed", string(key)) - } else { - expiredCertCount++ + certs, err := certDB.Items(0, 0) + if err != nil { + log.Error().Err(err).Msg("could not get certs from list") + } else { + for _, cert := range certs { + if !strings.EqualFold(cert.Domain, strings.TrimPrefix(mainDomainSuffix, ".")) { + if time.Unix(cert.ValidTill, 0).Before(threshold) { + err := certDB.Delete(cert.Domain) + if err != nil { + log.Error().Err(err).Msgf("Deleting expired certificate for %q failed", cert.Domain) + } else { + expiredCertCount++ + } } } } - key, resBytes, err = keyDatabaseIterator.Next() - } - log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount) + log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount) - // compact the database - msg, err := certDB.Compact() - if err != nil { - log.Error().Err(err).Msg("Compacting key database failed") - } else { - log.Debug().Msgf("Compacted key database: %s", msg) + // compact the database + msg, err := certDB.Compact() + if err != nil { + log.Error().Err(err).Msg("Compacting key database failed") + } else { + log.Debug().Msgf("Compacted key database: %s", msg) + } } // update main cert @@ -530,9 +528,10 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi log.Error().Msgf("Couldn't renew certificate for main domain %q expected main domain cert to exist, but it's missing - seems like the database is corrupted", mainDomainSuffix) } else { tlsCertificates, err := certcrypto.ParsePEMBundle(res.Certificate) - - // renew main certificate 30 days before it expires - if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { + if err != nil { + log.Error().Err(fmt.Errorf("could not parse cert for mainDomainSuffix: %w", err)) + } else if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { + // renew main certificate 30 days before it expires go (func() { _, err = obtainCert(mainDomainAcmeClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) if err != nil { diff --git a/server/certificates/mock.go b/server/certificates/mock.go index 0e87e6e..a28d0f4 100644 --- a/server/certificates/mock.go +++ b/server/certificates/mock.go @@ -13,14 +13,15 @@ import ( "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" + "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/database" ) -func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) tls.Certificate { +func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { key, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) if err != nil { - panic(err) + return nil, err } template := x509.Certificate{ @@ -52,7 +53,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) key, ) if err != nil { - panic(err) + return nil, err } out := &bytes.Buffer{} @@ -61,7 +62,7 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) Type: "CERTIFICATE", }) if err != nil { - panic(err) + return nil, err } outBytes := out.Bytes() res := &certificate.Resource{ @@ -75,12 +76,12 @@ func mockCert(domain, msg, mainDomainSuffix string, keyDatabase database.CertDB) databaseName = mainDomainSuffix } if err := keyDatabase.Put(databaseName, res); err != nil { - panic(err) + log.Error().Err(err) } tlsCertificate, err := tls.X509KeyPair(res.Certificate, res.PrivateKey) if err != nil { - panic(err) + return nil, err } - return tlsCertificate + return &tlsCertificate, nil } diff --git a/server/certificates/mock_test.go b/server/certificates/mock_test.go index 1cbd1f6..00e1b21 100644 --- a/server/certificates/mock_test.go +++ b/server/certificates/mock_test.go @@ -10,7 +10,8 @@ import ( func TestMockCert(t *testing.T) { db, err := database.NewTmpDB() assert.NoError(t, err) - cert := mockCert("example.com", "some error msg", "codeberg.page", db) + cert, err := mockCert("example.com", "some error msg", "codeberg.page", db) + assert.NoError(t, err) if assert.NotEmpty(t, cert) { assert.NotEmpty(t, cert.Certificate) } diff --git a/server/database/interface.go b/server/database/interface.go index 3ba3efc..56537a4 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -1,8 +1,11 @@ package database import ( - "github.com/akrylysov/pogreb" + "fmt" + + "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" + "github.com/rs/zerolog/log" ) type CertDB interface { @@ -10,6 +13,61 @@ type CertDB interface { Put(name string, cert *certificate.Resource) error Get(name string) (*certificate.Resource, error) Delete(key string) error + Items(page, pageSize int) ([]*Cert, error) + // Compact deprecated // TODO: remove in next version Compact() (string, error) - Items() *pogreb.ItemIterator +} + +type Cert struct { + Domain string `xorm:"pk NOT NULL UNIQUE 'domain'"` + Created int64 `xorm:"created NOT NULL DEFAULT 0 'created'"` + Updated int64 `xorm:"updated NOT NULL DEFAULT 0 'updated'"` + ValidTill int64 `xorm:" NOT NULL DEFAULT 0 'valid_till'"` + // certificate.Resource + CertURL string `xorm:"'cert_url'"` + CertStableURL string `xorm:"'cert_stable_url'"` + PrivateKey []byte `xorm:"'private_key'"` + Certificate []byte `xorm:"'certificate'"` + IssuerCertificate []byte `xorm:"'issuer_certificate'"` +} + +func (c Cert) Raw() *certificate.Resource { + return &certificate.Resource{ + Domain: c.Domain, + CertURL: c.CertURL, + CertStableURL: c.CertStableURL, + PrivateKey: c.PrivateKey, + Certificate: c.Certificate, + IssuerCertificate: c.IssuerCertificate, + } +} + +func toCert(name string, c *certificate.Resource) (*Cert, error) { + tlsCertificates, err := certcrypto.ParsePEMBundle(c.Certificate) + if err != nil { + return nil, err + } + if len(tlsCertificates) == 0 || tlsCertificates[0] == nil { + err := fmt.Errorf("parsed cert resource has no cert") + log.Error().Err(err).Str("domain", c.Domain).Msgf("cert: %v", c) + return nil, err + } + validTill := tlsCertificates[0].NotAfter.Unix() + + // TODO: do we need this or can we just go with domain name for wildcard cert + // default *.mock cert is prefixed with '.' + if name != c.Domain && name[1:] != c.Domain && name[0] != '.' { + return nil, fmt.Errorf("domain key and cert domain not equal") + } + + return &Cert{ + Domain: c.Domain, + ValidTill: validTill, + + CertURL: c.CertURL, + CertStableURL: c.CertStableURL, + PrivateKey: c.PrivateKey, + Certificate: c.Certificate, + IssuerCertificate: c.IssuerCertificate, + }, nil } diff --git a/server/database/mock.go b/server/database/mock.go index dfe2316..7c3735e 100644 --- a/server/database/mock.go +++ b/server/database/mock.go @@ -5,7 +5,6 @@ import ( "time" "github.com/OrlovEvgeny/go-mcache" - "github.com/akrylysov/pogreb" "github.com/go-acme/lego/v4/certificate" ) @@ -43,8 +42,8 @@ func (p tmpDB) Compact() (string, error) { return "Truncate done", nil } -func (p tmpDB) Items() *pogreb.ItemIterator { - panic("ItemIterator not implemented for tmpDB") +func (p tmpDB) Items(page, pageSize int) ([]*Cert, error) { + return nil, fmt.Errorf("items not implemented for tmpDB") } func NewTmpDB() (CertDB, error) { diff --git a/server/database/setup.go b/server/database/pogreb.go similarity index 76% rename from server/database/setup.go rename to server/database/pogreb.go index 097c63e..9a53faf 100644 --- a/server/database/setup.go +++ b/server/database/pogreb.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/gob" + "errors" "fmt" "time" @@ -62,8 +63,32 @@ func (p aDB) Compact() (string, error) { return fmt.Sprintf("%+v", result), nil } -func (p aDB) Items() *pogreb.ItemIterator { - return p.intern.Items() +func (p aDB) Items(_, _ int) ([]*Cert, error) { + items := make([]*Cert, 0, p.intern.Count()) + iterator := p.intern.Items() + for { + key, resBytes, err := iterator.Next() + if err != nil { + if errors.Is(err, pogreb.ErrIterationDone) { + break + } + return nil, err + } + + res := &certificate.Resource{} + if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(res); err != nil { + return nil, err + } + + cert, err := toCert(string(key), res) + if err != nil { + return nil, err + } + + items = append(items, cert) + } + + return items, nil } var _ CertDB = &aDB{} @@ -82,7 +107,7 @@ func (p aDB) sync() { } } -func New(path string) (CertDB, error) { +func NewPogreb(path string) (CertDB, error) { if path == "" { return nil, fmt.Errorf("path not set") } diff --git a/server/database/xorm.go b/server/database/xorm.go new file mode 100644 index 0000000..68ff18c --- /dev/null +++ b/server/database/xorm.go @@ -0,0 +1,121 @@ +package database + +import ( + "errors" + "fmt" + "strings" + + "github.com/rs/zerolog/log" + + "github.com/go-acme/lego/v4/certificate" + "xorm.io/xorm" + + // register sql driver + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" +) + +var _ CertDB = xDB{} + +var ErrNotFound = errors.New("entry not found") + +type xDB struct { + engine *xorm.Engine +} + +func NewXormDB(dbType, dbConn string) (CertDB, error) { + if !supportedDriver(dbType) { + return nil, fmt.Errorf("not supported db type '%s'", dbType) + } + if dbConn == "" { + return nil, fmt.Errorf("no db connection provided") + } + + e, err := xorm.NewEngine(dbType, dbConn) + if err != nil { + return nil, err + } + + if err := e.Sync2(new(Cert)); err != nil { + return nil, fmt.Errorf("cound not sync db model :%w", err) + } + + return &xDB{ + engine: e, + }, nil +} + +func (x xDB) Close() error { + return x.engine.Close() +} + +func (x xDB) Put(domain string, cert *certificate.Resource) error { + log.Trace().Str("domain", cert.Domain).Msg("inserting cert to db") + c, err := toCert(domain, cert) + if err != nil { + return err + } + + _, err = x.engine.Insert(c) + return err +} + +func (x xDB) Get(domain string) (*certificate.Resource, error) { + // TODO: do we need this or can we just go with domain name for wildcard cert + domain = strings.TrimPrefix(domain, ".") + + cert := new(Cert) + log.Trace().Str("domain", domain).Msg("get cert from db") + if found, err := x.engine.ID(domain).Get(cert); err != nil { + return nil, err + } else if !found { + return nil, fmt.Errorf("%w: name='%s'", ErrNotFound, domain) + } + return cert.Raw(), nil +} + +func (x xDB) Delete(domain string) error { + log.Trace().Str("domain", domain).Msg("delete cert from db") + _, err := x.engine.ID(domain).Delete(new(Cert)) + return err +} + +func (x xDB) Compact() (string, error) { + // not needed + return "", nil +} + +// Items return al certs from db, if pageSize is 0 it does not use limit +func (x xDB) Items(page, pageSize int) ([]*Cert, error) { + // paginated return + if pageSize > 0 { + certs := make([]*Cert, 0, pageSize) + if page >= 0 { + page = 1 + } + err := x.engine.Limit(pageSize, (page-1)*pageSize).Find(&certs) + return certs, err + } + + // return all + certs := make([]*Cert, 0, 64) + err := x.engine.Find(&certs) + return certs, err +} + +// Supported database drivers +const ( + DriverSqlite = "sqlite3" + DriverMysql = "mysql" + DriverPostgres = "postgres" +) + +func supportedDriver(driver string) bool { + switch driver { + case DriverMysql, DriverPostgres, DriverSqlite: + return true + default: + return false + } +} diff --git a/server/upstream/helper.go b/server/upstream/helper.go index a84d4f0..ac0ab3f 100644 --- a/server/upstream/helper.go +++ b/server/upstream/helper.go @@ -17,17 +17,17 @@ func (o *Options) GetBranchTimestamp(giteaClient *gitea.Client) (bool, error) { // Get default branch defaultBranch, err := giteaClient.GiteaGetRepoDefaultBranch(o.TargetOwner, o.TargetRepo) if err != nil { - log.Err(err).Msg("Could't fetch default branch from repository") + log.Err(err).Msg("Couldn't fetch default branch from repository") return false, err } - log.Debug().Msgf("Succesfully fetched default branch %q from Gitea", defaultBranch) + log.Debug().Msgf("Successfully fetched default branch %q from Gitea", defaultBranch) o.TargetBranch = defaultBranch } timestamp, err := giteaClient.GiteaGetRepoBranchTimestamp(o.TargetOwner, o.TargetRepo, o.TargetBranch) if err != nil { if !errors.Is(err, gitea.ErrorNotFound) { - log.Error().Err(err).Msg("Could not get latest commit's timestamp from branch") + log.Error().Err(err).Msg("Could not get latest commit timestamp from branch") } return false, err } @@ -36,7 +36,7 @@ func (o *Options) GetBranchTimestamp(giteaClient *gitea.Client) (bool, error) { return false, fmt.Errorf("empty response") } - log.Debug().Msgf("Succesfully fetched latest commit's timestamp from branch: %#v", timestamp) + log.Debug().Msgf("Successfully fetched latest commit timestamp from branch: %#v", timestamp) o.BranchTimestamp = timestamp.Timestamp o.TargetBranch = timestamp.Branch return true, nil From 1b6ea4b6e1b0b1fb79f3e2887f61f92c4c00330c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 10 Feb 2023 04:33:28 +0100 Subject: [PATCH 187/320] use same version var on cli app as header --- main.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index 8eac1fb..6c1d0cc 100644 --- a/main.go +++ b/main.go @@ -8,15 +8,13 @@ import ( "github.com/urfave/cli/v2" "codeberg.org/codeberg/pages/cmd" + "codeberg.org/codeberg/pages/server/version" ) -// can be changed with -X on compile -var version = "dev" - func main() { app := cli.NewApp() app.Name = "pages-server" - app.Version = version + app.Version = version.Version app.Usage = "pages server" app.Action = cmd.Serve app.Flags = cmd.ServerFlags From d8d119b0b3a4d557b3c1e1fd6cecb6efc5289901 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Feb 2023 00:31:56 +0000 Subject: [PATCH 188/320] Fix Cache Bug (#178) error io.EOF is gracefully end of file read. so we don't need to cancel cache saving Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/178 --- cmd/main.go | 4 ++-- server/gitea/cache.go | 2 +- server/upstream/domains.go | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index af4d2ce..4f0c019 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -44,7 +44,7 @@ func Serve(ctx *cli.Context) error { } log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) - giteaRoot := strings.TrimSuffix(ctx.String("gitea-root"), "/") + giteaRoot := ctx.String("gitea-root") giteaAPIToken := ctx.String("gitea-api-token") rawDomain := ctx.String("raw-domain") mainDomainSuffix := ctx.String("pages-domain") @@ -68,7 +68,7 @@ func Serve(ctx *cli.Context) error { allowedCorsDomains = append(allowedCorsDomains, rawDomain) } - // Make sure MainDomain has a trailing dot, and GiteaRoot has no trailing slash + // Make sure MainDomain has a trailing dot if !strings.HasPrefix(mainDomainSuffix, ".") { mainDomainSuffix = "." + mainDomainSuffix } diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 85cbcde..af61edf 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -80,7 +80,7 @@ type writeCacheReader struct { func (t *writeCacheReader) Read(p []byte) (n int, err error) { n, err = t.originalReader.Read(p) - if err != nil { + if err != nil && err != io.EOF { log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) t.hasError = true } else if n > 0 { diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 5b274b6..bb4b57a 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -1,6 +1,7 @@ package upstream import ( + "errors" "strings" "time" @@ -30,8 +31,8 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, } body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) - if err == nil || err == gitea.ErrorNotFound { - log.Info().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) + if err != nil && !errors.Is(err, gitea.ErrorNotFound) { + log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) } var domains []string From 272c7ca76fcc75e60ad9841b17f396dd5df63e6a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Feb 2023 01:26:21 +0000 Subject: [PATCH 189/320] Fix xorm regressions by handle wildcard certs correctly (#177) close #176 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/177 --- cmd/certs.go | 3 -- cmd/flags.go | 33 +++++++------ integration/get_test.go | 4 +- server/database/interface.go | 12 +++-- server/database/xorm.go | 45 ++++++++++++++++-- server/database/xorm_test.go | 92 ++++++++++++++++++++++++++++++++++++ server/upstream/domains.go | 2 +- 7 files changed, 162 insertions(+), 29 deletions(-) create mode 100644 server/database/xorm_test.go diff --git a/cmd/certs.go b/cmd/certs.go index 4adf076..96244b7 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -98,9 +98,6 @@ func listCerts(ctx *cli.Context) error { fmt.Printf("Domain\tValidTill\n\n") for _, cert := range items { - if cert.Domain[0] == '.' { - cert.Domain = "*" + cert.Domain - } fmt.Printf("%s\t%s\n", cert.Domain, time.Unix(cert.ValidTill, 0).Format(time.RFC3339)) diff --git a/cmd/flags.go b/cmd/flags.go index 40bb053..da3febc 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -43,6 +43,18 @@ var ( EnvVars: []string{"GITEA_API_TOKEN"}, Value: "", }, + &cli.BoolFlag{ + Name: "enable-lfs-support", + Usage: "enable lfs support, require gitea >= v1.17.0 as backend", + EnvVars: []string{"ENABLE_LFS_SUPPORT"}, + Value: true, + }, + &cli.BoolFlag{ + Name: "enable-symlink-support", + Usage: "follow symlinks if enabled, require gitea >= v1.18.0 as backend", + EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, + Value: true, + }, // ########################### // ### Page Server Domains ### @@ -73,7 +85,9 @@ var ( Value: "https://docs.codeberg.org/codeberg-pages/raw-content/", }, - // Server + // ######################### + // ### Page Server Setup ### + // ######################### &cli.StringFlag{ Name: "host", Usage: "specifies host of listening address", @@ -91,19 +105,6 @@ var ( // TODO: desc EnvVars: []string{"ENABLE_HTTP_SERVER"}, }, - // Server Options - &cli.BoolFlag{ - Name: "enable-lfs-support", - Usage: "enable lfs support, require gitea v1.17.0 as backend", - EnvVars: []string{"ENABLE_LFS_SUPPORT"}, - Value: true, - }, - &cli.BoolFlag{ - Name: "enable-symlink-support", - Usage: "follow symlinks if enabled, require gitea v1.18.0 as backend", - EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, - Value: true, - }, &cli.StringFlag{ Name: "log-level", Value: "warn", @@ -111,7 +112,9 @@ var ( EnvVars: []string{"LOG_LEVEL"}, }, - // ACME + // ############################ + // ### ACME Client Settings ### + // ############################ &cli.StringFlag{ Name: "acme-api-endpoint", EnvVars: []string{"ACME_API"}, diff --git a/integration/get_test.go b/integration/get_test.go index 6dd57bc..55e6d12 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -20,7 +20,9 @@ func TestGetRedirect(t *testing.T) { log.Println("=== TestGetRedirect ===") // test custom domain redirect resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430") - assert.NoError(t, err) + if !assert.NoError(t, err) { + t.FailNow() + } if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) { t.FailNow() } diff --git a/server/database/interface.go b/server/database/interface.go index 56537a4..a0edc91 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -54,10 +54,14 @@ func toCert(name string, c *certificate.Resource) (*Cert, error) { } validTill := tlsCertificates[0].NotAfter.Unix() - // TODO: do we need this or can we just go with domain name for wildcard cert - // default *.mock cert is prefixed with '.' - if name != c.Domain && name[1:] != c.Domain && name[0] != '.' { - return nil, fmt.Errorf("domain key and cert domain not equal") + // handle wildcard certs + if name[:1] == "." { + name = "*" + name + } + if name != c.Domain { + err := fmt.Errorf("domain key '%s' and cert domain '%s' not equal", name, c.Domain) + log.Error().Err(err).Msg("toCert conversion did discover mismatch") + // TODO: fail hard: return nil, err } return &Cert{ diff --git a/server/database/xorm.go b/server/database/xorm.go index 68ff18c..a14f887 100644 --- a/server/database/xorm.go +++ b/server/database/xorm.go @@ -3,7 +3,6 @@ package database import ( "errors" "fmt" - "strings" "github.com/rs/zerolog/log" @@ -52,18 +51,38 @@ func (x xDB) Close() error { func (x xDB) Put(domain string, cert *certificate.Resource) error { log.Trace().Str("domain", cert.Domain).Msg("inserting cert to db") + + domain = integrationTestReplacements(domain) c, err := toCert(domain, cert) if err != nil { return err } - _, err = x.engine.Insert(c) - return err + sess := x.engine.NewSession() + if err := sess.Begin(); err != nil { + return err + } + defer sess.Close() + + if exist, _ := sess.ID(c.Domain).Exist(); exist { + if _, err := sess.ID(c.Domain).Update(c); err != nil { + return err + } + } else { + if _, err = sess.Insert(c); err != nil { + return err + } + } + + return sess.Commit() } func (x xDB) Get(domain string) (*certificate.Resource, error) { - // TODO: do we need this or can we just go with domain name for wildcard cert - domain = strings.TrimPrefix(domain, ".") + // handle wildcard certs + if domain[:1] == "." { + domain = "*" + domain + } + domain = integrationTestReplacements(domain) cert := new(Cert) log.Trace().Str("domain", domain).Msg("get cert from db") @@ -76,6 +95,12 @@ func (x xDB) Get(domain string) (*certificate.Resource, error) { } func (x xDB) Delete(domain string) error { + // handle wildcard certs + if domain[:1] == "." { + domain = "*" + domain + } + domain = integrationTestReplacements(domain) + log.Trace().Str("domain", domain).Msg("delete cert from db") _, err := x.engine.ID(domain).Delete(new(Cert)) return err @@ -119,3 +144,13 @@ func supportedDriver(driver string) bool { return false } } + +// integrationTestReplacements is needed because integration tests use a single domain cert, +// while production use a wildcard cert +// TODO: find a better way to handle this +func integrationTestReplacements(domainKey string) string { + if domainKey == "*.localhost.mock.directory" { + return "localhost.mock.directory" + } + return domainKey +} diff --git a/server/database/xorm_test.go b/server/database/xorm_test.go new file mode 100644 index 0000000..9c032ee --- /dev/null +++ b/server/database/xorm_test.go @@ -0,0 +1,92 @@ +package database + +import ( + "errors" + "testing" + + "github.com/go-acme/lego/v4/certificate" + "github.com/stretchr/testify/assert" + "xorm.io/xorm" +) + +func newTestDB(t *testing.T) *xDB { + e, err := xorm.NewEngine("sqlite3", ":memory:") + assert.NoError(t, err) + assert.NoError(t, e.Sync2(new(Cert))) + return &xDB{engine: e} +} + +func TestSanitizeWildcardCerts(t *testing.T) { + certDB := newTestDB(t) + + _, err := certDB.Get(".not.found") + assert.True(t, errors.Is(err, ErrNotFound)) + + // TODO: cert key and domain mismatch are don not fail hard jet + // https://codeberg.org/Codeberg/pages-server/src/commit/d8595cee882e53d7f44f1ddc4ef8a1f7b8f31d8d/server/database/interface.go#L64 + // + // assert.Error(t, certDB.Put(".wildcard.de", &certificate.Resource{ + // Domain: "*.localhost.mock.directory", + // Certificate: localhost_mock_directory_certificate, + // })) + + // insert new wildcard cert + assert.NoError(t, certDB.Put(".wildcard.de", &certificate.Resource{ + Domain: "*.wildcard.de", + Certificate: localhost_mock_directory_certificate, + })) + + // update existing cert + assert.Error(t, certDB.Put(".wildcard.de", &certificate.Resource{ + Domain: "*.wildcard.de", + Certificate: localhost_mock_directory_certificate, + })) + + c1, err := certDB.Get(".wildcard.de") + assert.NoError(t, err) + c2, err := certDB.Get("*.wildcard.de") + assert.NoError(t, err) + assert.EqualValues(t, c1, c2) +} + +var localhost_mock_directory_certificate = []byte(`-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgIIJyBaXHmLk6gwDQYJKoZIhvcNAQELBQAwKDEmMCQGA1UE +AxMdUGViYmxlIEludGVybWVkaWF0ZSBDQSA0OWE0ZmIwHhcNMjMwMjEwMDEwOTA2 +WhcNMjgwMjEwMDEwOTA2WjAjMSEwHwYDVQQDExhsb2NhbGhvc3QubW9jay5kaXJl +Y3RvcnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIU/CjzS7t62Gj +neEMqvP7sn99ULT7AEUzEfWL05fWG2z714qcUg1hXkZLgdVDgmsCpplyddip7+2t +ZH/9rLPLMqJphzvOL4CF6jDLbeifETtKyjnt9vUZFnnNWcP3tu8lo8iYSl08qsUI +Pp/hiEriAQzCDjTbR5m9xUPNPYqxzcS4ALzmmCX9Qfc4CuuhMkdv2G4TT7rylWrA +SCSRPnGjeA7pCByfNrO/uXbxmzl3sMO3k5sqgMkx1QIHEN412V8+vtx88mt2sM6k +xjzGZWWKXlRq+oufIKX9KPplhsCjMH6E3VNAzgOPYDqXagtUcGmLWghURltO8Mt2 +zwM6OgjjAgMBAAGjgaUwgaIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsG +AQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSMQvlJ1755 +sarf8i1KNqj7s5o/aDAfBgNVHSMEGDAWgBTcZcxJMhWdP7MecHCCpNkFURC/YzAj +BgNVHREEHDAaghhsb2NhbGhvc3QubW9jay5kaXJlY3RvcnkwDQYJKoZIhvcNAQEL +BQADggEBACcd7TT28OWwzQN2PcH0aG38JX5Wp2iOS/unDCfWjNAztXHW7nBDMxza +VtyebkJfccexpuVuOsjOX+bww0vtEYIvKX3/GbkhogksBrNkE0sJZtMnZWMR33wa +YxAy/kJBTmLi02r8fX9ZhwjldStHKBav4USuP7DXZjrgX7LFQhR4LIDrPaYqQRZ8 +ltC3mM9LDQ9rQyIFP5cSBMO3RUAm4I8JyLoOdb/9G2uxjHr7r6eG1g8DmLYSKBsQ +mWGQDOYgR3cGltDe2yMxM++yHY+b1uhxGOWMrDA1+1k7yI19LL8Ifi2FMovDfu/X +JxYk1NNNtdctwaYJFenmGQvDaIq1KgE= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDUDCCAjigAwIBAgIIKBJ7IIA6W1swDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVUGViYmxlIFJvb3QgQ0EgNTdmZjE2MCAXDTIzMDIwOTA1MzMxMloYDzIwNTMw +MjA5MDUzMzEyWjAoMSYwJAYDVQQDEx1QZWJibGUgSW50ZXJtZWRpYXRlIENBIDQ5 +YTRmYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOvlqRx8SXQFWo2 +gFCiXxls53eENcyr8+meFyjgnS853eEvplaPxoa2MREKd+ZYxM8EMMfj2XGvR3UI +aqR5QyLQ9ihuRqvQo4fG91usBHgH+vDbGPdMX8gDmm9HgnmtOVhSKJU+M2jfE1SW +UuWB9xOa3LMreTXbTNfZEMoXf+GcWZMbx5WPgEga3DvfmV+RsfNvB55eD7YAyZgF +ZnQ3Dskmnxxlkz0EGgd7rqhFHHNB9jARlL22gITADwoWZidlr3ciM9DISymRKQ0c +mRN15fQjNWdtuREgJlpXecbYQMGhdTOmFrqdHkveD1o63rGSC4z+s/APV6xIbcRp +aNpO7L8CAwEAAaOBgzCBgDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYB +BQUHAwEGCCsGAQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNxlzEky +FZ0/sx5wcIKk2QVREL9jMB8GA1UdIwQYMBaAFOqfkm9rebIz4z0SDIKW5edLg5JM +MA0GCSqGSIb3DQEBCwUAA4IBAQBRG9AHEnyj2fKzVDDbQaKHjAF5jh0gwyHoIeRK +FkP9mQNSWxhvPWI0tK/E49LopzmVuzSbDd5kZsaii73rAs6f6Rf9W5veo3AFSEad +stM+Zv0f2vWB38nuvkoCRLXMX+QUeuL65rKxdEpyArBju4L3/PqAZRgMLcrH+ak8 +nvw5RdAq+Km/ZWyJgGikK6cfMmh91YALCDFnoWUWrCjkBaBFKrG59ONV9f0IQX07 +aNfFXFCF5l466xw9dHjw5iaFib10cpY3iq4kyPYIMs6uaewkCtxWKKjiozM4g4w3 +HqwyUyZ52WUJOJ/6G9DJLDtN3fgGR+IAp8BhYd5CqOscnt3h +-----END CERTIFICATE-----`) diff --git a/server/upstream/domains.go b/server/upstream/domains.go index bb4b57a..eb30394 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -49,7 +49,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, } } - // Add [owner].[pages-domain] as valid domnain. + // Add [owner].[pages-domain] as valid domain. domains = append(domains, o.TargetOwner+mainDomainSuffix) if domains[len(domains)-1] == actualDomain { valid = true From fd643d15f0f44081a6469be7613b1162b8a29258 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Feb 2023 02:04:57 +0000 Subject: [PATCH 190/320] Drop: pogreb support (#175) followup of #173 close #95 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/175 --- .woodpecker.yml | 13 --- Justfile | 2 +- cmd/certs.go | 61 +------------ cmd/flags.go | 9 +- cmd/setup.go | 26 +----- go.mod | 1 - go.sum | 2 - server/certificates/certificates.go | 8 -- server/database/interface.go | 2 - server/database/mock.go | 5 -- server/database/pogreb.go | 134 ---------------------------- server/database/xorm.go | 5 -- 12 files changed, 6 insertions(+), 262 deletions(-) delete mode 100644 server/database/pogreb.go diff --git a/.woodpecker.yml b/.woodpecker.yml index ba44f82..5b07053 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -65,19 +65,6 @@ pipeline: - RAW_DOMAIN=raw.localhost.mock.directory - PORT=4430 - # TODO: remove in next version - integration-tests-legacy: - group: test - image: codeberg.org/6543/docker-images/golang_just - commands: - - just integration - environment: - - ACME_API=https://acme.mock.directory - - PAGES_DOMAIN=localhost.mock.directory - - RAW_DOMAIN=raw.localhost.mock.directory - - PORT=4430 - - DB_TYPE= - release: image: plugins/gitea-release settings: diff --git a/Justfile b/Justfile index 683222d..0db7845 100644 --- a/Justfile +++ b/Justfile @@ -27,7 +27,7 @@ fmt: tool-gofumpt clean: go clean ./... - rm -rf build/ integration/certs.sqlite integration/key-database.pogreb/ integration/acme-account.json + rm -rf build/ integration/certs.sqlite integration/acme-account.json tool-golangci: @hash golangci-lint> /dev/null 2>&1; if [ $? -ne 0 ]; then \ diff --git a/cmd/certs.go b/cmd/certs.go index 96244b7..6012b6e 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -4,11 +4,7 @@ import ( "fmt" "time" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" - - "codeberg.org/codeberg/pages/server/database" ) var Certs = &cli.Command{ @@ -25,63 +21,8 @@ var Certs = &cli.Command{ Usage: "remove a certificate from the database", Action: removeCert, }, - { - Name: "migrate", - Usage: "migrate from \"pogreb\" driver to dbms driver", - Action: migrateCerts, - }, }, - Flags: append(CertStorageFlags, []cli.Flag{ - &cli.BoolFlag{ - Name: "verbose", - Usage: "print trace info", - EnvVars: []string{"VERBOSE"}, - Value: false, - }, - }...), -} - -func migrateCerts(ctx *cli.Context) error { - dbType := ctx.String("db-type") - if dbType == "" { - dbType = "sqlite3" - } - dbConn := ctx.String("db-conn") - dbPogrebConn := ctx.String("db-pogreb") - verbose := ctx.Bool("verbose") - - log.Level(zerolog.InfoLevel) - if verbose { - log.Level(zerolog.TraceLevel) - } - - xormDB, err := database.NewXormDB(dbType, dbConn) - if err != nil { - return fmt.Errorf("could not connect to database: %w", err) - } - defer xormDB.Close() - - pogrebDB, err := database.NewPogreb(dbPogrebConn) - if err != nil { - return fmt.Errorf("could not open database: %w", err) - } - defer pogrebDB.Close() - - fmt.Printf("Start migration from \"%s\" to \"%s:%s\" ...\n", dbPogrebConn, dbType, dbConn) - - certs, err := pogrebDB.Items(0, 0) - if err != nil { - return err - } - - for _, cert := range certs { - if err := xormDB.Put(cert.Domain, cert.Raw()); err != nil { - return err - } - } - - fmt.Println("... done") - return nil + Flags: CertStorageFlags, } func listCerts(ctx *cli.Context) error { diff --git a/cmd/flags.go b/cmd/flags.go index da3febc..a37b179 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -6,16 +6,9 @@ import ( var ( CertStorageFlags = []cli.Flag{ - &cli.StringFlag{ - // TODO: remove in next version - // DEPRICATED - Name: "db-pogreb", - Value: "key-database.pogreb", - EnvVars: []string{"DB_POGREB"}, - }, &cli.StringFlag{ Name: "db-type", - Value: "", // TODO: "sqlite3" in next version + Value: "sqlite3", EnvVars: []string{"DB_TYPE"}, }, &cli.StringFlag{ diff --git a/cmd/setup.go b/cmd/setup.go index 3d1d6ee..bccba03 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -10,29 +10,9 @@ import ( ) func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) { - if ctx.String("db-type") != "" { - log.Trace().Msg("use xorm mode") - certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) - if err != nil { - return nil, nil, fmt.Errorf("could not connect to database: %w", err) - } - } else { - // TODO: remove in next version - fmt.Println(` -###################### -## W A R N I N G !!! # -###################### - -You use "pogreb" witch is deprecated and will be removed in the next version. -Please switch to sqlite, mysql or postgres !!! - -The simplest way is, to use './pages certs migrate' and set environment var DB_TYPE to 'sqlite' on next start.`) - log.Error().Msg("depricated \"pogreb\" used\n") - - certDB, err = database.NewPogreb(ctx.String("db-pogreb")) - if err != nil { - return nil, nil, fmt.Errorf("could not create database: %w", err) - } + certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) + if err != nil { + return nil, nil, fmt.Errorf("could not connect to database: %w", err) } closeFn = func() { diff --git a/go.mod b/go.mod index 2e75c8e..944e2ad 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.20 require ( code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a - github.com/akrylysov/pogreb v0.10.1 github.com/go-acme/lego/v4 v4.5.3 github.com/go-sql-driver/mysql v1.6.0 github.com/joho/godotenv v1.4.0 diff --git a/go.sum b/go.sum index c1042c9..b5a7568 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,6 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs= github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= -github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= -github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 555539e..23fad17 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -510,14 +510,6 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } } log.Debug().Msgf("Removed %d expired certificates from the database", expiredCertCount) - - // compact the database - msg, err := certDB.Compact() - if err != nil { - log.Error().Err(err).Msg("Compacting key database failed") - } else { - log.Debug().Msgf("Compacted key database: %s", msg) - } } // update main cert diff --git a/server/database/interface.go b/server/database/interface.go index a0edc91..92068c5 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -14,8 +14,6 @@ type CertDB interface { Get(name string) (*certificate.Resource, error) Delete(key string) error Items(page, pageSize int) ([]*Cert, error) - // Compact deprecated // TODO: remove in next version - Compact() (string, error) } type Cert struct { diff --git a/server/database/mock.go b/server/database/mock.go index 7c3735e..6148287 100644 --- a/server/database/mock.go +++ b/server/database/mock.go @@ -37,11 +37,6 @@ func (p tmpDB) Delete(key string) error { return nil } -func (p tmpDB) Compact() (string, error) { - p.intern.Truncate() - return "Truncate done", nil -} - func (p tmpDB) Items(page, pageSize int) ([]*Cert, error) { return nil, fmt.Errorf("items not implemented for tmpDB") } diff --git a/server/database/pogreb.go b/server/database/pogreb.go deleted file mode 100644 index 9a53faf..0000000 --- a/server/database/pogreb.go +++ /dev/null @@ -1,134 +0,0 @@ -package database - -import ( - "bytes" - "context" - "encoding/gob" - "errors" - "fmt" - "time" - - "github.com/akrylysov/pogreb" - "github.com/akrylysov/pogreb/fs" - "github.com/go-acme/lego/v4/certificate" - "github.com/rs/zerolog/log" -) - -var _ CertDB = aDB{} - -type aDB struct { - ctx context.Context - cancel context.CancelFunc - intern *pogreb.DB - syncInterval time.Duration -} - -func (p aDB) Close() error { - p.cancel() - return p.intern.Sync() -} - -func (p aDB) Put(name string, cert *certificate.Resource) error { - var resGob bytes.Buffer - if err := gob.NewEncoder(&resGob).Encode(cert); err != nil { - return err - } - return p.intern.Put([]byte(name), resGob.Bytes()) -} - -func (p aDB) Get(name string) (*certificate.Resource, error) { - cert := &certificate.Resource{} - resBytes, err := p.intern.Get([]byte(name)) - if err != nil { - return nil, err - } - if resBytes == nil { - return nil, nil - } - if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(cert); err != nil { - return nil, err - } - return cert, nil -} - -func (p aDB) Delete(key string) error { - return p.intern.Delete([]byte(key)) -} - -func (p aDB) Compact() (string, error) { - result, err := p.intern.Compact() - if err != nil { - return "", err - } - return fmt.Sprintf("%+v", result), nil -} - -func (p aDB) Items(_, _ int) ([]*Cert, error) { - items := make([]*Cert, 0, p.intern.Count()) - iterator := p.intern.Items() - for { - key, resBytes, err := iterator.Next() - if err != nil { - if errors.Is(err, pogreb.ErrIterationDone) { - break - } - return nil, err - } - - res := &certificate.Resource{} - if err := gob.NewDecoder(bytes.NewBuffer(resBytes)).Decode(res); err != nil { - return nil, err - } - - cert, err := toCert(string(key), res) - if err != nil { - return nil, err - } - - items = append(items, cert) - } - - return items, nil -} - -var _ CertDB = &aDB{} - -func (p aDB) sync() { - for { - err := p.intern.Sync() - if err != nil { - log.Error().Err(err).Msg("Syncing cert database failed") - } - select { - case <-p.ctx.Done(): - return - case <-time.After(p.syncInterval): - } - } -} - -func NewPogreb(path string) (CertDB, error) { - if path == "" { - return nil, fmt.Errorf("path not set") - } - db, err := pogreb.Open(path, &pogreb.Options{ - BackgroundSyncInterval: 30 * time.Second, - BackgroundCompactionInterval: 6 * time.Hour, - FileSystem: fs.OSMMap, - }) - if err != nil { - return nil, err - } - - ctx, cancel := context.WithCancel(context.Background()) - result := &aDB{ - ctx: ctx, - cancel: cancel, - intern: db, - syncInterval: 5 * time.Minute, - } - - go result.sync() - - return result, nil -} diff --git a/server/database/xorm.go b/server/database/xorm.go index a14f887..4b43cbb 100644 --- a/server/database/xorm.go +++ b/server/database/xorm.go @@ -106,11 +106,6 @@ func (x xDB) Delete(domain string) error { return err } -func (x xDB) Compact() (string, error) { - // not needed - return "", nil -} - // Items return al certs from db, if pageSize is 0 it does not use limit func (x xDB) Items(page, pageSize int) ([]*Cert, error) { // paginated return From 5753f7136deb4cc29bae91640f0ae1ef1472b8f6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Feb 2023 02:29:08 +0000 Subject: [PATCH 191/320] Move acmeClient creation into own file & struct (#179) get rid of gobal vars and make make functions with less args :) tldr: collect funcs and create a own ACME client to manage that stuff Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/179 --- cmd/flags.go | 10 +- cmd/main.go | 35 +--- cmd/setup.go | 34 +++ server/certificates/acme_client.go | 95 +++++++++ server/certificates/acme_config.go | 100 +++++++++ server/certificates/cached_challengers.go | 41 ++++ server/certificates/certificates.go | 242 +++------------------- server/certificates/mock_test.go | 3 +- 8 files changed, 323 insertions(+), 237 deletions(-) create mode 100644 server/certificates/acme_client.go create mode 100644 server/certificates/acme_config.go create mode 100644 server/certificates/cached_challengers.go diff --git a/cmd/flags.go b/cmd/flags.go index a37b179..8052421 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -140,9 +140,15 @@ var ( EnvVars: []string{"ACME_EAB_HMAC"}, }, &cli.StringFlag{ - Name: "dns-provider", - // TODO: Usage + Name: "dns-provider", + Usage: "Use DNS-Challenge for main domain\n\nRead more at: https://go-acme.github.io/lego/dns/", EnvVars: []string{"DNS_PROVIDER"}, }, + &cli.StringFlag{ + Name: "acme-account-config", + Usage: "json file of acme account", + Value: "acme-account.json", + EnvVars: []string{"ACME_ACCOUNT_CONFIG"}, + }, }...) ) diff --git a/cmd/main.go b/cmd/main.go index 4f0c019..a1c3b97 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,7 +3,6 @@ package cmd import ( "context" "crypto/tls" - "errors" "fmt" "net" "net/http" @@ -52,17 +51,6 @@ func Serve(ctx *cli.Context) error { listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) enableHTTPServer := ctx.Bool("enable-http-server") - acmeAPI := ctx.String("acme-api-endpoint") - acmeMail := ctx.String("acme-email") - acmeUseRateLimits := ctx.Bool("acme-use-rate-limits") - acmeAcceptTerms := ctx.Bool("acme-accept-terms") - acmeEabKID := ctx.String("acme-eab-kid") - acmeEabHmac := ctx.String("acme-eab-hmac") - dnsProvider := ctx.String("dns-provider") - if (!acmeAcceptTerms || dnsProvider == "") && acmeAPI != "https://acme.mock.directory" { - return errors.New("you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory") - } - allowedCorsDomains := AllowedCorsDomains if rawDomain != "" { allowedCorsDomains = append(allowedCorsDomains, rawDomain) @@ -94,6 +82,15 @@ func Serve(ctx *cli.Context) error { return fmt.Errorf("could not create new gitea client: %v", err) } + acmeClient, err := createAcmeClient(ctx, enableHTTPServer, challengeCache) + if err != nil { + return err + } + + if err := certificates.SetupMainDomainCertificates(mainDomainSuffix, acmeClient, certDB); err != nil { + return err + } + // Create handler based on settings httpsHandler := handler.Handler(mainDomainSuffix, rawDomain, giteaClient, @@ -112,24 +109,14 @@ func Serve(ctx *cli.Context) error { listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaClient, - dnsProvider, - acmeUseRateLimits, + acmeClient, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, certDB)) - acmeConfig, err := certificates.SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeAcceptTerms) - if err != nil { - return err - } - - if err := certificates.SetupCertificates(mainDomainSuffix, dnsProvider, acmeConfig, acmeUseRateLimits, enableHTTPServer, challengeCache, certDB); err != nil { - return err - } - interval := 12 * time.Hour certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) defer cancelCertMaintain() - go certificates.MaintainCertDB(certMaintainCtx, interval, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB) + go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, mainDomainSuffix, certDB) if enableHTTPServer { go func() { diff --git a/cmd/setup.go b/cmd/setup.go index bccba03..bb9f8cb 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -1,14 +1,19 @@ package cmd import ( + "errors" "fmt" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/database" ) +var ErrAcmeMissConfig = errors.New("ACME client has wrong config") + func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) { certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) if err != nil { @@ -23,3 +28,32 @@ func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err e return certDB, closeFn, nil } + +func createAcmeClient(ctx *cli.Context, enableHTTPServer bool, challengeCache cache.SetGetKey) (*certificates.AcmeClient, error) { + acmeAPI := ctx.String("acme-api-endpoint") + acmeMail := ctx.String("acme-email") + acmeEabHmac := ctx.String("acme-eab-hmac") + acmeEabKID := ctx.String("acme-eab-kid") + acmeAcceptTerms := ctx.Bool("acme-accept-terms") + dnsProvider := ctx.String("dns-provider") + acmeUseRateLimits := ctx.Bool("acme-use-rate-limits") + acmeAccountConf := ctx.String("acme-account-config") + + // check config + if (!acmeAcceptTerms || dnsProvider == "") && acmeAPI != "https://acme.mock.directory" { + return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) + } + + return certificates.NewAcmeClient( + acmeAccountConf, + acmeAPI, + acmeMail, + acmeEabHmac, + acmeEabKID, + dnsProvider, + acmeAcceptTerms, + enableHTTPServer, + acmeUseRateLimits, + challengeCache, + ) +} diff --git a/server/certificates/acme_client.go b/server/certificates/acme_client.go new file mode 100644 index 0000000..7737396 --- /dev/null +++ b/server/certificates/acme_client.go @@ -0,0 +1,95 @@ +package certificates + +import ( + "fmt" + "sync" + "time" + + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/providers/dns" + "github.com/reugn/equalizer" + "github.com/rs/zerolog/log" + + "codeberg.org/codeberg/pages/server/cache" +) + +type AcmeClient struct { + legoClient *lego.Client + dnsChallengerLegoClient *lego.Client + + obtainLocks sync.Map + + acmeUseRateLimits bool + + // limiter + acmeClientOrderLimit *equalizer.TokenBucket + acmeClientRequestLimit *equalizer.TokenBucket + acmeClientFailLimit *equalizer.TokenBucket + acmeClientCertificateLimitPerUser map[string]*equalizer.TokenBucket +} + +func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeAcceptTerms, enableHTTPServer, acmeUseRateLimits bool, challengeCache cache.SetGetKey) (*AcmeClient, error) { + acmeConfig, err := setupAcmeConfig(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeAcceptTerms) + if err != nil { + return nil, err + } + + acmeClient, err := lego.NewClient(acmeConfig) + if err != nil { + log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + } else { + err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) + if err != nil { + log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") + } + if enableHTTPServer { + err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) + if err != nil { + log.Error().Err(err).Msg("Can't create HTTP-01 provider") + } + } + } + + mainDomainAcmeClient, err := lego.NewClient(acmeConfig) + if err != nil { + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + } else { + if dnsProvider == "" { + // using mock server, don't use wildcard certs + err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) + if err != nil { + log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") + } + } else { + // use DNS-Challenge https://go-acme.github.io/lego/dns/ + provider, err := dns.NewDNSChallengeProviderByName(dnsProvider) + if err != nil { + return nil, fmt.Errorf("can not create DNS Challenge provider: %w", err) + } + if err := mainDomainAcmeClient.Challenge.SetDNS01Provider(provider); err != nil { + return nil, fmt.Errorf("can not create DNS-01 provider: %w", err) + } + } + } + + return &AcmeClient{ + legoClient: acmeClient, + dnsChallengerLegoClient: mainDomainAcmeClient, + + acmeUseRateLimits: acmeUseRateLimits, + + obtainLocks: sync.Map{}, + + // limiter + + // rate limit is 300 / 3 hours, we want 200 / 2 hours but to refill more often, so that's 25 new domains every 15 minutes + // TODO: when this is used a lot, we probably have to think of a somewhat better solution? + acmeClientOrderLimit: equalizer.NewTokenBucket(25, 15*time.Minute), + // rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) + acmeClientRequestLimit: equalizer.NewTokenBucket(5, 1*time.Second), + // rate limit is 5 / hour https://letsencrypt.org/docs/failed-validation-limit/ + acmeClientFailLimit: equalizer.NewTokenBucket(5, 1*time.Hour), + // checkUserLimit() use this to rate als per user + acmeClientCertificateLimitPerUser: map[string]*equalizer.TokenBucket{}, + }, nil +} diff --git a/server/certificates/acme_config.go b/server/certificates/acme_config.go new file mode 100644 index 0000000..69568e6 --- /dev/null +++ b/server/certificates/acme_config.go @@ -0,0 +1,100 @@ +package certificates + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/json" + "fmt" + "os" + + "github.com/go-acme/lego/v4/certcrypto" + "github.com/go-acme/lego/v4/lego" + "github.com/go-acme/lego/v4/registration" + "github.com/rs/zerolog/log" +) + +func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { + var myAcmeAccount AcmeAccount + var myAcmeConfig *lego.Config + + if account, err := os.ReadFile(configFile); err == nil { + log.Info().Msgf("found existing acme account config file '%s'", configFile) + if err := json.Unmarshal(account, &myAcmeAccount); err != nil { + return nil, err + } + myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM)) + if err != nil { + return nil, err + } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) + myAcmeConfig.CADirURL = acmeAPI + myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + + // Validate Config + _, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Info().Err(err).Msg("config validation failed, you might just delete the config file and let it recreate") + return nil, fmt.Errorf("acme config validation failed: %w", err) + } + return myAcmeConfig, nil + } else if !os.IsNotExist(err) { + return nil, err + } + + log.Info().Msgf("no existing acme account config found, try to create a new one") + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + myAcmeAccount = AcmeAccount{ + Email: acmeMail, + Key: privateKey, + KeyPEM: string(certcrypto.PEMEncode(privateKey)), + } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) + myAcmeConfig.CADirURL = acmeAPI + myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 + tempClient, err := lego.NewClient(myAcmeConfig) + if err != nil { + log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") + } else { + // accept terms & log in to EAB + if acmeEabKID == "" || acmeEabHmac == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) + if err != nil { + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") + } else { + myAcmeAccount.Registration = reg + } + } else { + reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ + TermsOfServiceAgreed: acmeAcceptTerms, + Kid: acmeEabKID, + HmacEncoded: acmeEabHmac, + }) + if err != nil { + log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") + } else { + myAcmeAccount.Registration = reg + } + } + + if myAcmeAccount.Registration != nil { + acmeAccountJSON, err := json.Marshal(myAcmeAccount) + if err != nil { + log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") + select {} + } + log.Info().Msgf("new acme account created. write to config file '%s'", configFile) + err = os.WriteFile(configFile, acmeAccountJSON, 0o600) + if err != nil { + log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") + select {} + } + } + } + + return myAcmeConfig, nil +} diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go new file mode 100644 index 0000000..6ce6e67 --- /dev/null +++ b/server/certificates/cached_challengers.go @@ -0,0 +1,41 @@ +package certificates + +import ( + "time" + + "github.com/go-acme/lego/v4/challenge" + + "codeberg.org/codeberg/pages/server/cache" +) + +type AcmeTLSChallengeProvider struct { + challengeCache cache.SetGetKey +} + +// make sure AcmeTLSChallengeProvider match Provider interface +var _ challenge.Provider = AcmeTLSChallengeProvider{} + +func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { + return a.challengeCache.Set(domain, keyAuth, 1*time.Hour) +} + +func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { + a.challengeCache.Remove(domain) + return nil +} + +type AcmeHTTPChallengeProvider struct { + challengeCache cache.SetGetKey +} + +// make sure AcmeHTTPChallengeProvider match Provider interface +var _ challenge.Provider = AcmeHTTPChallengeProvider{} + +func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { + return a.challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) +} + +func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { + a.challengeCache.Remove(domain + "/" + token) + return nil +} diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 23fad17..3ea440f 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -2,27 +2,18 @@ package certificates import ( "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" "crypto/tls" "crypto/x509" - "encoding/json" "errors" "fmt" - "os" "strconv" "strings" - "sync" "time" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/certificate" - "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" - "github.com/go-acme/lego/v4/providers/dns" - "github.com/go-acme/lego/v4/registration" "github.com/reugn/equalizer" "github.com/rs/zerolog/log" @@ -33,11 +24,12 @@ import ( "codeberg.org/codeberg/pages/server/upstream" ) +var ErrUserRateLimitExceeded = errors.New("rate limit exceeded: 10 certificates per user per 24 hours") + // TLSConfig returns the configuration for generating, serving and cleaning up Let's Encrypt certificates. func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, - dnsProvider string, - acmeUseRateLimits bool, + acmeClient *AcmeClient, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, certDB database.CertDB, ) *tls.Config { @@ -100,7 +92,7 @@ func TLSConfig(mainDomainSuffix string, var tlsCertificate *tls.Certificate var err error - if tlsCertificate, err = retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider, acmeUseRateLimits, certDB); err != nil { + if tlsCertificate, err = acmeClient.retrieveCertFromDB(sni, mainDomainSuffix, false, certDB); err != nil { // request a new certificate if strings.EqualFold(sni, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") @@ -110,7 +102,7 @@ func TLSConfig(mainDomainSuffix string, return nil, fmt.Errorf("won't request certificate for %q", sni) } - tlsCertificate, err = obtainCert(acmeClient, []string{sni}, nil, targetOwner, dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + tlsCertificate, err = acmeClient.obtainCert(acmeClient.legoClient, []string{sni}, nil, targetOwner, false, mainDomainSuffix, certDB) if err != nil { return nil, err } @@ -141,67 +133,20 @@ func TLSConfig(mainDomainSuffix string, } } -func checkUserLimit(user string) error { - userLimit, ok := acmeClientCertificateLimitPerUser[user] +func (c *AcmeClient) checkUserLimit(user string) error { + userLimit, ok := c.acmeClientCertificateLimitPerUser[user] if !ok { - // Each Codeberg user can only add 10 new domains per day. + // Each user can only add 10 new domains per day. userLimit = equalizer.NewTokenBucket(10, time.Hour*24) - acmeClientCertificateLimitPerUser[user] = userLimit + c.acmeClientCertificateLimitPerUser[user] = userLimit } if !userLimit.Ask() { - return errors.New("rate limit exceeded: 10 certificates per user per 24 hours") + return fmt.Errorf("user '%s' error: %w", user, ErrUserRateLimitExceeded) } return nil } -var ( - acmeClient, mainDomainAcmeClient *lego.Client - acmeClientCertificateLimitPerUser = map[string]*equalizer.TokenBucket{} -) - -// rate limit is 300 / 3 hours, we want 200 / 2 hours but to refill more often, so that's 25 new domains every 15 minutes -// TODO: when this is used a lot, we probably have to think of a somewhat better solution? -var acmeClientOrderLimit = equalizer.NewTokenBucket(25, 15*time.Minute) - -// rate limit is 20 / second, we want 5 / second (especially as one cert takes at least two requests) -var acmeClientRequestLimit = equalizer.NewTokenBucket(5, 1*time.Second) - -// rate limit is 5 / hour https://letsencrypt.org/docs/failed-validation-limit/ -var acmeClientFailLimit = equalizer.NewTokenBucket(5, 1*time.Hour) - -type AcmeTLSChallengeProvider struct { - challengeCache cache.SetGetKey -} - -// make sure AcmeTLSChallengeProvider match Provider interface -var _ challenge.Provider = AcmeTLSChallengeProvider{} - -func (a AcmeTLSChallengeProvider) Present(domain, _, keyAuth string) error { - return a.challengeCache.Set(domain, keyAuth, 1*time.Hour) -} - -func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { - a.challengeCache.Remove(domain) - return nil -} - -type AcmeHTTPChallengeProvider struct { - challengeCache cache.SetGetKey -} - -// make sure AcmeHTTPChallengeProvider match Provider interface -var _ challenge.Provider = AcmeHTTPChallengeProvider{} - -func (a AcmeHTTPChallengeProvider) Present(domain, token, keyAuth string) error { - return a.challengeCache.Set(domain+"/"+token, keyAuth, 1*time.Hour) -} - -func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { - a.challengeCache.Remove(domain + "/" + token) - return nil -} - -func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) (*tls.Certificate, error) { +func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) { // parse certificate from database res, err := certDB.Get(sni) if err != nil { @@ -235,7 +180,7 @@ func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLi // TODO: make a queue ? go (func() { res.CSR = nil // acme client doesn't like CSR to be set - if _, err := obtainCert(acmeClient, []string{sni}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB); err != nil { + if _, err := c.obtainCert(c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil { log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) } })() @@ -245,28 +190,26 @@ func retrieveCertFromDB(sni, mainDomainSuffix, dnsProvider string, acmeUseRateLi return &tlsCertificate, nil } -var obtainLocks = sync.Map{} - -func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user, dnsProvider, mainDomainSuffix string, acmeUseRateLimits bool, keyDatabase database.CertDB) (*tls.Certificate, error) { +func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") - if dnsProvider == "" && len(domains[0]) > 0 && domains[0][0] == '*' { + if useDnsProvider && len(domains[0]) > 0 && domains[0][0] == '*' { domains = domains[1:] } // lock to avoid simultaneous requests - _, working := obtainLocks.LoadOrStore(name, struct{}{}) + _, working := c.obtainLocks.LoadOrStore(name, struct{}{}) if working { for working { time.Sleep(100 * time.Millisecond) - _, working = obtainLocks.Load(name) + _, working = c.obtainLocks.Load(name) } - cert, err := retrieveCertFromDB(name, mainDomainSuffix, dnsProvider, acmeUseRateLimits, keyDatabase) + cert, err := c.retrieveCertFromDB(name, mainDomainSuffix, useDnsProvider, keyDatabase) if err != nil { return nil, fmt.Errorf("certificate failed in synchronous request: %w", err) } return cert, nil } - defer obtainLocks.Delete(name) + defer c.obtainLocks.Delete(name) if acmeClient == nil { return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase) @@ -276,29 +219,29 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re var res *certificate.Resource var err error if renew != nil && renew.CertURL != "" { - if acmeUseRateLimits { - acmeClientRequestLimit.Take() + if c.acmeUseRateLimits { + c.acmeClientRequestLimit.Take() } log.Debug().Msgf("Renewing certificate for: %v", domains) res, err = acmeClient.Certificate.Renew(*renew, true, false, "") if err != nil { log.Error().Err(err).Msgf("Couldn't renew certificate for %v, trying to request a new one", domains) - if acmeUseRateLimits { - acmeClientFailLimit.Take() + if c.acmeUseRateLimits { + c.acmeClientFailLimit.Take() } res = nil } } if res == nil { if user != "" { - if err := checkUserLimit(user); err != nil { + if err := c.checkUserLimit(user); err != nil { return nil, err } } - if acmeUseRateLimits { - acmeClientOrderLimit.Take() - acmeClientRequestLimit.Take() + if c.acmeUseRateLimits { + c.acmeClientOrderLimit.Take() + c.acmeClientRequestLimit.Take() } log.Debug().Msgf("Re-requesting new certificate for %v", domains) res, err = acmeClient.Certificate.Obtain(certificate.ObtainRequest{ @@ -306,8 +249,8 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re Bundle: true, MustStaple: false, }) - if acmeUseRateLimits && err != nil { - acmeClientFailLimit.Take() + if c.acmeUseRateLimits && err != nil { + c.acmeClientFailLimit.Take() } } if err != nil { @@ -349,136 +292,15 @@ func obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Re return &tlsCertificate, nil } -func SetupAcmeConfig(acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { - // TODO: make it a config flag - const configFile = "acme-account.json" - var myAcmeAccount AcmeAccount - var myAcmeConfig *lego.Config - - if account, err := os.ReadFile(configFile); err == nil { - if err := json.Unmarshal(account, &myAcmeAccount); err != nil { - return nil, err - } - myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM)) - if err != nil { - return nil, err - } - myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = acmeAPI - myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - - // Validate Config - _, err := lego.NewClient(myAcmeConfig) - if err != nil { - // TODO: should we fail hard instead? - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") - } - return myAcmeConfig, nil - } else if !os.IsNotExist(err) { - return nil, err - } - - privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, err - } - myAcmeAccount = AcmeAccount{ - Email: acmeMail, - Key: privateKey, - KeyPEM: string(certcrypto.PEMEncode(privateKey)), - } - myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = acmeAPI - myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 - tempClient, err := lego.NewClient(myAcmeConfig) - if err != nil { - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") - } else { - // accept terms & log in to EAB - if acmeEabKID == "" || acmeEabHmac == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) - if err != nil { - log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") - } else { - myAcmeAccount.Registration = reg - } - } else { - reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: acmeAcceptTerms, - Kid: acmeEabKID, - HmacEncoded: acmeEabHmac, - }) - if err != nil { - log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") - } else { - myAcmeAccount.Registration = reg - } - } - - if myAcmeAccount.Registration != nil { - acmeAccountJSON, err := json.Marshal(myAcmeAccount) - if err != nil { - log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") - select {} - } - err = os.WriteFile(configFile, acmeAccountJSON, 0o600) - if err != nil { - log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") - select {} - } - } - } - - return myAcmeConfig, nil -} - -func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Config, acmeUseRateLimits, enableHTTPServer bool, challengeCache cache.SetGetKey, certDB database.CertDB) error { +func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits mainCertBytes, err := certDB.Get(mainDomainSuffix) if err != nil && !errors.Is(err, database.ErrNotFound) { return fmt.Errorf("cert database is not working: %w", err) } - acmeClient, err = lego.NewClient(acmeConfig) - if err != nil { - log.Fatal().Err(err).Msg("Can't create ACME client, continuing with mock certs only") - } else { - err = acmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") - } - if enableHTTPServer { - err = acmeClient.Challenge.SetHTTP01Provider(AcmeHTTPChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create HTTP-01 provider") - } - } - } - - mainDomainAcmeClient, err = lego.NewClient(acmeConfig) - if err != nil { - log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") - } else { - if dnsProvider == "" { - // using mock server, don't use wildcard certs - err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") - } - } else { - provider, err := dns.NewDNSChallengeProviderByName(dnsProvider) - if err != nil { - log.Error().Err(err).Msg("Can't create DNS Challenge provider") - } - err = mainDomainAcmeClient.Challenge.SetDNS01Provider(provider) - if err != nil { - log.Error().Err(err).Msg("Can't create DNS-01 provider") - } - } - } - if mainCertBytes == nil { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only") } @@ -487,7 +309,7 @@ func SetupCertificates(mainDomainSuffix, dnsProvider string, acmeConfig *lego.Co return nil } -func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffix, dnsProvider string, acmeUseRateLimits bool, certDB database.CertDB) { +func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) { for { // delete expired certs that will be invalid until next clean up threshold := time.Now().Add(interval) @@ -525,7 +347,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, mainDomainSuffi } else if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { // renew main certificate 30 days before it expires go (func() { - _, err = obtainCert(mainDomainAcmeClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", dnsProvider, mainDomainSuffix, acmeUseRateLimits, certDB) + _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew certificate for main domain") } diff --git a/server/certificates/mock_test.go b/server/certificates/mock_test.go index 00e1b21..5d0dde0 100644 --- a/server/certificates/mock_test.go +++ b/server/certificates/mock_test.go @@ -3,8 +3,9 @@ package certificates import ( "testing" - "codeberg.org/codeberg/pages/server/database" "github.com/stretchr/testify/assert" + + "codeberg.org/codeberg/pages/server/database" ) func TestMockCert(t *testing.T) { From 08d4e70cfd4df37748b29a859b1e5d408d93dd59 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Feb 2023 03:39:38 +0100 Subject: [PATCH 192/320] Update Readme to point out new Architecture --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6d5a294..5707d65 100644 --- a/README.md +++ b/README.md @@ -87,20 +87,20 @@ You can also contact the maintainers of this project: ### First steps -The code of this repository is split in several modules. -While heavy refactoring work is currently undergo, you can easily understand the basic structure: +The code of this repository is split in several modules. +The [Architecture is explained](https://codeberg.org/Codeberg/pages-server/wiki/Architecture) in the wiki. + The `cmd` folder holds the data necessary for interacting with the service via the cli. -If you are considering to deploy the service yourself, make sure to check it out. The heart of the software lives in the `server` folder and is split in several modules. -After scanning the code, you should quickly be able to understand their function and start hacking on them. Again: Feel free to get in touch with us for any questions that might arise. Thank you very much. - ### Test Server -run `just dev` +Make sure you have [golang](https://go.dev) v1.20 or newer and [just](https://just.systems/man/en/) installed. + +run `just dev` now this pages should work: - https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg - https://momar.localhost.mock.directory:4430/ci-testing/ From 46316f9e2f472b070d187ee1e06742dea0739413 Mon Sep 17 00:00:00 2001 From: crystal Date: Sat, 11 Feb 2023 03:12:42 +0000 Subject: [PATCH 193/320] Fix raw domain for branches with custom domains and index.html (#159) fix #156 fix #157 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/159 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crystal Co-committed-by: crystal --- integration/get_test.go | 28 ++++++++++++++++++++++++++++ server/handler/try.go | 2 +- server/upstream/upstream.go | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 55e6d12..b70eeed 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -109,6 +109,34 @@ func TestCustomDomainRedirects(t *testing.T) { assert.EqualValues(t, "https://mock-pages.codeberg-test.org/README.md", resp.Header.Get("Location")) } +func TestRawCustomDomain(t *testing.T) { + log.Println("=== TestRawCustomDomain ===") + // test raw domain response for custom domain branch + resp, err := getTestHTTPSClient().Get("https://raw.localhost.mock.directory:4430/cb_pages_tests/raw-test/example") // need cb_pages_tests fork + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) + assert.EqualValues(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "76", resp.Header.Get("Content-Length")) + assert.EqualValues(t, 76, getSize(resp.Body)) +} + +func TestRawIndex(t *testing.T) { + log.Println("=== TestRawCustomDomain ===") + // test raw domain response for index.html + resp, err := getTestHTTPSClient().Get("https://raw.localhost.mock.directory:4430/cb_pages_tests/raw-test/@branch-test/index.html") // need cb_pages_tests fork + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) + assert.EqualValues(t, "text/plain; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "597", resp.Header.Get("Content-Length")) + assert.EqualValues(t, 597, getSize(resp.Body)) +} + func TestGetNotFound(t *testing.T) { log.Println("=== TestGetNotFound ===") // test custom not found pages diff --git a/server/handler/try.go b/server/handler/try.go index 5a09b91..5c65138 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -20,7 +20,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, canonicalDomainCache cache.SetGetKey, ) { // check if a canonical domain exists on a request on MainDomain - if strings.HasSuffix(trimmedHost, mainDomainSuffix) { + if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw { canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", mainDomainSuffix, canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix) { canonicalPath := ctx.Req.RequestURI diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 7c3c848..3845969 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -168,7 +168,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin ctx.Redirect(ctx.Path()+"/", http.StatusTemporaryRedirect) return true } - if strings.HasSuffix(ctx.Path(), "/index.html") { + if strings.HasSuffix(ctx.Path(), "/index.html") && !o.ServeRaw { ctx.Redirect(strings.TrimSuffix(ctx.Path(), "index.html"), http.StatusTemporaryRedirect) return true } From 9a3d1c36dce7c7d9c460dfdb684f6e4bae5848f9 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 13 Feb 2023 20:14:45 +0000 Subject: [PATCH 194/320] Document more flags & make http port customizable (#183) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/183 --- .gitignore | 1 + Justfile | 3 ++ cmd/flags.go | 34 +++++++++++------- cmd/main.go | 40 +++++++++++---------- cmd/setup.go | 5 +++ server/certificates/acme_config.go | 2 ++ server/certificates/cached_challengers.go | 19 ++++++++++ server/certificates/certificates.go | 43 +++++++++++++---------- server/setup.go | 27 -------------- 9 files changed, 97 insertions(+), 77 deletions(-) delete mode 100644 server/setup.go diff --git a/.gitignore b/.gitignore index 8745935..3035107 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ vendor/ pages certs.sqlite +.bash_history diff --git a/Justfile b/Justfile index 0db7845..9ee7eb3 100644 --- a/Justfile +++ b/Justfile @@ -50,3 +50,6 @@ integration: integration-run TEST: go test -race -tags 'integration {{TAGS}}' -run "^{{TEST}}$" codeberg.org/codeberg/pages/integration/... + +docker: + docker run --rm -it --user $(id -u) -v $(pwd):/work --workdir /work -e HOME=/work codeberg.org/6543/docker-images/golang_just diff --git a/cmd/flags.go b/cmd/flags.go index 8052421..5bc638b 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -8,11 +8,13 @@ var ( CertStorageFlags = []cli.Flag{ &cli.StringFlag{ Name: "db-type", + Usage: "Specify the database driver. Valid options are \"sqlite3\", \"mysql\" and \"postgres\". Read more at https://xorm.io", Value: "sqlite3", EnvVars: []string{"DB_TYPE"}, }, &cli.StringFlag{ Name: "db-conn", + Usage: "Specify the database connection. For \"sqlite3\" it's the filepath. Read more at https://go.dev/doc/tutorial/database-access", Value: "certs.sqlite", EnvVars: []string{"DB_CONN"}, }, @@ -87,15 +89,21 @@ var ( EnvVars: []string{"HOST"}, Value: "[::]", }, - &cli.StringFlag{ + &cli.UintFlag{ Name: "port", - Usage: "specifies port of listening address", - EnvVars: []string{"PORT"}, - Value: "443", + Usage: "specifies the https port to listen to ssl requests", + EnvVars: []string{"PORT", "HTTPS_PORT"}, + Value: 443, + }, + &cli.UintFlag{ + Name: "http-port", + Usage: "specifies the http port, you also have to enable http server via ENABLE_HTTP_SERVER=true", + EnvVars: []string{"HTTP_PORT"}, + Value: 80, }, &cli.BoolFlag{ - Name: "enable-http-server", - // TODO: desc + Name: "enable-http-server", + Usage: "start a http server to redirect to https and respond to http acme challenges", EnvVars: []string{"ENABLE_HTTP_SERVER"}, }, &cli.StringFlag{ @@ -125,23 +133,23 @@ var ( Value: true, }, &cli.BoolFlag{ - Name: "acme-accept-terms", - // TODO: Usage + Name: "acme-accept-terms", + Usage: "To accept the ACME ToS", EnvVars: []string{"ACME_ACCEPT_TERMS"}, }, &cli.StringFlag{ - Name: "acme-eab-kid", - // TODO: Usage + Name: "acme-eab-kid", + Usage: "Register the current account to the ACME server with external binding.", EnvVars: []string{"ACME_EAB_KID"}, }, &cli.StringFlag{ - Name: "acme-eab-hmac", - // TODO: Usage + Name: "acme-eab-hmac", + Usage: "Register the current account to the ACME server with external binding.", EnvVars: []string{"ACME_EAB_HMAC"}, }, &cli.StringFlag{ Name: "dns-provider", - Usage: "Use DNS-Challenge for main domain\n\nRead more at: https://go-acme.github.io/lego/dns/", + Usage: "Use DNS-Challenge for main domain. Read more at: https://go-acme.github.io/lego/dns/", EnvVars: []string{"DNS_PROVIDER"}, }, &cli.StringFlag{ diff --git a/cmd/main.go b/cmd/main.go index a1c3b97..8a65d43 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -14,7 +14,6 @@ import ( "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" - "codeberg.org/codeberg/pages/server" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/certificates" "codeberg.org/codeberg/pages/server/gitea" @@ -48,7 +47,9 @@ func Serve(ctx *cli.Context) error { rawDomain := ctx.String("raw-domain") mainDomainSuffix := ctx.String("pages-domain") rawInfoPage := ctx.String("raw-info-page") - listeningAddress := fmt.Sprintf("%s:%s", ctx.String("host"), ctx.String("port")) + listeningHost := ctx.String("host") + listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("port")) + listeningHTTPAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("http-port")) enableHTTPServer := ctx.Bool("enable-http-server") allowedCorsDomains := AllowedCorsDomains @@ -91,22 +92,14 @@ func Serve(ctx *cli.Context) error { return err } - // Create handler based on settings - httpsHandler := handler.Handler(mainDomainSuffix, rawDomain, - giteaClient, - rawInfoPage, - BlacklistedPaths, allowedCorsDomains, - dnsLookupCache, canonicalDomainCache) - - httpHandler := server.SetupHTTPACMEChallengeServer(challengeCache) - - // Setup listener and TLS - log.Info().Msgf("Listening on https://%s", listeningAddress) - listener, err := net.Listen("tcp", listeningAddress) + // Create listener for SSL connections + log.Info().Msgf("Listening on https://%s", listeningSSLAddress) + listener, err := net.Listen("tcp", listeningSSLAddress) if err != nil { return fmt.Errorf("couldn't create listener: %v", err) } + // Setup listener for SSL connections listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaClient, acmeClient, @@ -119,18 +112,29 @@ func Serve(ctx *cli.Context) error { go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, mainDomainSuffix, certDB) if enableHTTPServer { + // Create handler for http->https redirect and http acme challenges + httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache) + + // Create listener for http and start listening go func() { - log.Info().Msg("Start HTTP server listening on :80") - err := http.ListenAndServe("[::]:80", httpHandler) + log.Info().Msgf("Start HTTP server listening on %s", listeningHTTPAddress) + err := http.ListenAndServe(listeningHTTPAddress, httpHandler) if err != nil { log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") } }() } - // Start the web fastServer + // Create ssl handler based on settings + sslHandler := handler.Handler(mainDomainSuffix, rawDomain, + giteaClient, + rawInfoPage, + BlacklistedPaths, allowedCorsDomains, + dnsLookupCache, canonicalDomainCache) + + // Start the ssl listener log.Info().Msgf("Start listening on %s", listener.Addr()) - if err := http.Serve(listener, httpsHandler); err != nil { + if err := http.Serve(listener, sslHandler); err != nil { log.Panic().Err(err).Msg("Couldn't start fastServer") } diff --git a/cmd/setup.go b/cmd/setup.go index bb9f8cb..cde4bc9 100644 --- a/cmd/setup.go +++ b/cmd/setup.go @@ -43,6 +43,11 @@ func createAcmeClient(ctx *cli.Context, enableHTTPServer bool, challengeCache ca if (!acmeAcceptTerms || dnsProvider == "") && acmeAPI != "https://acme.mock.directory" { return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) } + if acmeEabHmac != "" && acmeEabKID == "" { + return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) + } else if acmeEabHmac == "" && acmeEabKID != "" { + return nil, fmt.Errorf("%w: ACME_EAB_KID also needs ACME_EAB_HMAC to be set", ErrAcmeMissConfig) + } return certificates.NewAcmeClient( acmeAccountConf, diff --git a/server/certificates/acme_config.go b/server/certificates/acme_config.go index 69568e6..12ad7c6 100644 --- a/server/certificates/acme_config.go +++ b/server/certificates/acme_config.go @@ -14,6 +14,8 @@ import ( "github.com/rs/zerolog/log" ) +const challengePath = "/.well-known/acme-challenge/" + func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { var myAcmeAccount AcmeAccount var myAcmeConfig *lego.Config diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go index 6ce6e67..02474b3 100644 --- a/server/certificates/cached_challengers.go +++ b/server/certificates/cached_challengers.go @@ -1,11 +1,15 @@ package certificates import ( + "net/http" + "strings" "time" "github.com/go-acme/lego/v4/challenge" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/utils" ) type AcmeTLSChallengeProvider struct { @@ -39,3 +43,18 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { a.challengeCache.Remove(domain + "/" + token) return nil } + +func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + ctx := context.New(w, req) + if strings.HasPrefix(ctx.Path(), challengePath) { + challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) + if !ok || challenge == nil { + ctx.String("no challenge for this token", http.StatusNotFound) + } + ctx.String(challenge.(string)) + } else { + ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently) + } + } +} diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 3ea440f..707672c 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -36,22 +36,23 @@ func TLSConfig(mainDomainSuffix string, return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { - sni := strings.ToLower(strings.TrimSpace(info.ServerName)) - if len(sni) < 1 { - return nil, errors.New("missing sni") + domain := strings.ToLower(strings.TrimSpace(info.ServerName)) + if len(domain) < 1 { + return nil, errors.New("missing domain info via SNI (RFC 4366, Section 3.1)") } + // https request init is actually a acme challenge if info.SupportedProtos != nil { for _, proto := range info.SupportedProtos { if proto != tlsalpn01.ACMETLS1Protocol { continue } - challenge, ok := challengeCache.Get(sni) + challenge, ok := challengeCache.Get(domain) if !ok { return nil, errors.New("no challenge for this domain") } - cert, err := tlsalpn01.ChallengeCert(sni, challenge.(string)) + cert, err := tlsalpn01.ChallengeCert(domain, challenge.(string)) if err != nil { return nil, err } @@ -61,22 +62,22 @@ func TLSConfig(mainDomainSuffix string, targetOwner := "" mayObtainCert := true - if strings.HasSuffix(sni, mainDomainSuffix) || strings.EqualFold(sni, mainDomainSuffix[1:]) { + if strings.HasSuffix(domain, mainDomainSuffix) || strings.EqualFold(domain, mainDomainSuffix[1:]) { // deliver default certificate for the main domain (*.codeberg.page) - sni = mainDomainSuffix + domain = mainDomainSuffix } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(sni, mainDomainSuffix, dnsLookupCache) + targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs - sni = mainDomainSuffix + domain = mainDomainSuffix } else { targetOpt := &upstream.Options{ TargetOwner: targetOwner, TargetRepo: targetRepo, TargetBranch: targetBranch, } - _, valid := targetOpt.CheckCanonicalDomain(giteaClient, sni, mainDomainSuffix, canonicalDomainCache) + _, valid := targetOpt.CheckCanonicalDomain(giteaClient, domain, mainDomainSuffix, canonicalDomainCache) if !valid { // We shouldn't obtain a certificate when we cannot check if the // repository has specified this domain in the `.domains` file. @@ -85,30 +86,34 @@ func TLSConfig(mainDomainSuffix string, } } - if tlsCertificate, ok := keyCache.Get(sni); ok { + if tlsCertificate, ok := keyCache.Get(domain); ok { // we can use an existing certificate object return tlsCertificate.(*tls.Certificate), nil } var tlsCertificate *tls.Certificate var err error - if tlsCertificate, err = acmeClient.retrieveCertFromDB(sni, mainDomainSuffix, false, certDB); err != nil { - // request a new certificate - if strings.EqualFold(sni, mainDomainSuffix) { + if tlsCertificate, err = acmeClient.retrieveCertFromDB(domain, mainDomainSuffix, false, certDB); err != nil { + if !errors.Is(err, database.ErrNotFound) { + return nil, err + } + // we could not find a cert in db, request a new certificate + + // first check if we are allowed to obtain a cert for this domain + if strings.EqualFold(domain, mainDomainSuffix) { return nil, errors.New("won't request certificate for main domain, something really bad has happened") } - if !mayObtainCert { - return nil, fmt.Errorf("won't request certificate for %q", sni) + return nil, fmt.Errorf("won't request certificate for %q", domain) } - tlsCertificate, err = acmeClient.obtainCert(acmeClient.legoClient, []string{sni}, nil, targetOwner, false, mainDomainSuffix, certDB) + tlsCertificate, err = acmeClient.obtainCert(acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB) if err != nil { return nil, err } } - if err := keyCache.Set(sni, tlsCertificate, 15*time.Minute); err != nil { + if err := keyCache.Set(domain, tlsCertificate, 15*time.Minute); err != nil { return nil, err } return tlsCertificate, nil @@ -164,7 +169,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv if !strings.EqualFold(sni, mainDomainSuffix) { tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) if err != nil { - return nil, fmt.Errorf("error parsin leaf tlsCert: %w", err) + return nil, fmt.Errorf("error parsing leaf tlsCert: %w", err) } // renew certificates 7 days before they expire diff --git a/server/setup.go b/server/setup.go deleted file mode 100644 index 282e692..0000000 --- a/server/setup.go +++ /dev/null @@ -1,27 +0,0 @@ -package server - -import ( - "net/http" - "strings" - - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/context" - "codeberg.org/codeberg/pages/server/utils" -) - -func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc { - challengePath := "/.well-known/acme-challenge/" - - return func(w http.ResponseWriter, req *http.Request) { - ctx := context.New(w, req) - if strings.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) - if !ok || challenge == nil { - ctx.String("no challenge for this token", http.StatusNotFound) - } - ctx.String(challenge.(string)) - } else { - ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently) - } - } -} From 42b3f8d1b7933e67a2a24867d1189d522f2fb884 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 13 Feb 2023 23:13:30 +0000 Subject: [PATCH 195/320] use mockery for mock code generation (#185) close #181 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/185 --- server/certificates/mock_test.go | 6 +- server/database/interface.go | 3 + server/database/mock.go | 139 +++++++++++++++++++++++-------- 3 files changed, 113 insertions(+), 35 deletions(-) diff --git a/server/certificates/mock_test.go b/server/certificates/mock_test.go index 5d0dde0..644e8a9 100644 --- a/server/certificates/mock_test.go +++ b/server/certificates/mock_test.go @@ -4,13 +4,15 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "codeberg.org/codeberg/pages/server/database" ) func TestMockCert(t *testing.T) { - db, err := database.NewTmpDB() - assert.NoError(t, err) + db := database.NewMockCertDB(t) + db.Mock.On("Put", mock.Anything, mock.Anything).Return(nil) + cert, err := mockCert("example.com", "some error msg", "codeberg.page", db) assert.NoError(t, err) if assert.NotEmpty(t, cert) { diff --git a/server/database/interface.go b/server/database/interface.go index 92068c5..7fdbae7 100644 --- a/server/database/interface.go +++ b/server/database/interface.go @@ -8,6 +8,9 @@ import ( "github.com/rs/zerolog/log" ) +//go:generate go install github.com/vektra/mockery/v2@latest +//go:generate mockery --name CertDB --output . --filename mock.go --inpackage --case underscore + type CertDB interface { Close() error Put(name string, cert *certificate.Resource) error diff --git a/server/database/mock.go b/server/database/mock.go index 6148287..e7e2c38 100644 --- a/server/database/mock.go +++ b/server/database/mock.go @@ -1,49 +1,122 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + package database import ( - "fmt" - "time" - - "github.com/OrlovEvgeny/go-mcache" - "github.com/go-acme/lego/v4/certificate" + certificate "github.com/go-acme/lego/v4/certificate" + mock "github.com/stretchr/testify/mock" ) -var _ CertDB = tmpDB{} - -type tmpDB struct { - intern *mcache.CacheDriver - ttl time.Duration +// MockCertDB is an autogenerated mock type for the CertDB type +type MockCertDB struct { + mock.Mock } -func (p tmpDB) Close() error { - _ = p.intern.Close() - return nil -} +// Close provides a mock function with given fields: +func (_m *MockCertDB) Close() error { + ret := _m.Called() -func (p tmpDB) Put(name string, cert *certificate.Resource) error { - return p.intern.Set(name, cert, p.ttl) -} - -func (p tmpDB) Get(name string) (*certificate.Resource, error) { - cert, has := p.intern.Get(name) - if !has { - return nil, fmt.Errorf("cert for %q not found", name) + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) } - return cert.(*certificate.Resource), nil + + return r0 } -func (p tmpDB) Delete(key string) error { - p.intern.Remove(key) - return nil +// Delete provides a mock function with given fields: key +func (_m *MockCertDB) Delete(key string) error { + ret := _m.Called(key) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(key) + } else { + r0 = ret.Error(0) + } + + return r0 } -func (p tmpDB) Items(page, pageSize int) ([]*Cert, error) { - return nil, fmt.Errorf("items not implemented for tmpDB") +// Get provides a mock function with given fields: name +func (_m *MockCertDB) Get(name string) (*certificate.Resource, error) { + ret := _m.Called(name) + + var r0 *certificate.Resource + var r1 error + if rf, ok := ret.Get(0).(func(string) (*certificate.Resource, error)); ok { + return rf(name) + } + if rf, ok := ret.Get(0).(func(string) *certificate.Resource); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*certificate.Resource) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } -func NewTmpDB() (CertDB, error) { - return &tmpDB{ - intern: mcache.New(), - ttl: time.Minute, - }, nil +// Items provides a mock function with given fields: page, pageSize +func (_m *MockCertDB) Items(page int, pageSize int) ([]*Cert, error) { + ret := _m.Called(page, pageSize) + + var r0 []*Cert + var r1 error + if rf, ok := ret.Get(0).(func(int, int) ([]*Cert, error)); ok { + return rf(page, pageSize) + } + if rf, ok := ret.Get(0).(func(int, int) []*Cert); ok { + r0 = rf(page, pageSize) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*Cert) + } + } + + if rf, ok := ret.Get(1).(func(int, int) error); ok { + r1 = rf(page, pageSize) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Put provides a mock function with given fields: name, cert +func (_m *MockCertDB) Put(name string, cert *certificate.Resource) error { + ret := _m.Called(name, cert) + + var r0 error + if rf, ok := ret.Get(0).(func(string, *certificate.Resource) error); ok { + r0 = rf(name, cert) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewMockCertDB interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockCertDB creates a new instance of MockCertDB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockCertDB(t mockConstructorTestingTNewMockCertDB) *MockCertDB { + mock := &MockCertDB{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock } From 0adac9a5b15746a139e76e1de3996d4a20e791fa Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 14 Feb 2023 02:23:28 +0000 Subject: [PATCH 196/320] fix http -> https redirect and add integration tests for it (#184) and more logging Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/184 --- Justfile | 2 ++ cmd/main.go | 9 ++++--- integration/get_test.go | 12 +++++++++ integration/main_test.go | 2 ++ server/certificates/cached_challengers.go | 33 +++++++++++++++++++---- server/certificates/certificates.go | 1 + 6 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Justfile b/Justfile index 9ee7eb3..0b8f814 100644 --- a/Justfile +++ b/Justfile @@ -9,6 +9,8 @@ dev: export PAGES_DOMAIN=localhost.mock.directory export RAW_DOMAIN=raw.localhost.mock.directory export PORT=4430 + export HTTP_PORT=8880 + export ENABLE_HTTP_SERVER=true export LOG_LEVEL=trace go run -tags '{{TAGS}}' . diff --git a/cmd/main.go b/cmd/main.go index 8a65d43..aa00f54 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,8 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix := ctx.String("pages-domain") rawInfoPage := ctx.String("raw-info-page") listeningHost := ctx.String("host") - listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("port")) + listeningSSLPort := ctx.Uint("port") + listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort) listeningHTTPAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("http-port")) enableHTTPServer := ctx.Bool("enable-http-server") @@ -93,7 +94,7 @@ func Serve(ctx *cli.Context) error { } // Create listener for SSL connections - log.Info().Msgf("Listening on https://%s", listeningSSLAddress) + log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress) listener, err := net.Listen("tcp", listeningSSLAddress) if err != nil { return fmt.Errorf("couldn't create listener: %v", err) @@ -113,7 +114,7 @@ func Serve(ctx *cli.Context) error { if enableHTTPServer { // Create handler for http->https redirect and http acme challenges - httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache) + httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, listeningSSLPort) // Create listener for http and start listening go func() { @@ -133,7 +134,7 @@ func Serve(ctx *cli.Context) error { dnsLookupCache, canonicalDomainCache) // Start the ssl listener - log.Info().Msgf("Start listening on %s", listener.Addr()) + log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) if err := http.Serve(listener, sslHandler); err != nil { log.Panic().Err(err).Msg("Couldn't start fastServer") } diff --git a/integration/get_test.go b/integration/get_test.go index b70eeed..3a7190a 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -193,6 +193,18 @@ func TestGetOptions(t *testing.T) { assert.EqualValues(t, "GET, HEAD, OPTIONS", resp.Header.Get("Allow")) } +func TestHttpRedirect(t *testing.T) { + log.Println("=== TestHttpRedirect ===") + resp, err := getTestHTTPSClient().Get("http://mock-pages.codeberg-test.org:8880/README.md") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusMovedPermanently, resp.StatusCode) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "https://mock-pages.codeberg-test.org:4430/README.md", resp.Header.Get("Location")) +} + func getTestHTTPSClient() *http.Client { cookieJar, _ := cookiejar.New(nil) return &http.Client{ diff --git a/integration/main_test.go b/integration/main_test.go index 3e0e187..a6579fd 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -40,6 +40,8 @@ func startServer(ctx context.Context) error { setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") setEnvIfNotSet("PORT", "4430") + setEnvIfNotSet("HTTP_PORT", "8880") + setEnvIfNotSet("ENABLE_HTTP_SERVER", "true") setEnvIfNotSet("DB_TYPE", "sqlite3") app := cli.NewApp() diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go index 02474b3..bc9ea67 100644 --- a/server/certificates/cached_challengers.go +++ b/server/certificates/cached_challengers.go @@ -1,15 +1,17 @@ package certificates import ( + "fmt" "net/http" + "net/url" "strings" "time" "github.com/go-acme/lego/v4/challenge" + "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/context" - "codeberg.org/codeberg/pages/server/utils" ) type AcmeTLSChallengeProvider struct { @@ -44,17 +46,38 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey) http.HandlerFunc { +func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey, sslPort uint) http.HandlerFunc { + // handle custom-ssl-ports to be added on https redirects + portPart := "" + if sslPort != 443 { + portPart = fmt.Sprintf(":%d", sslPort) + } + return func(w http.ResponseWriter, req *http.Request) { ctx := context.New(w, req) + domain := ctx.TrimHostPort() + + // it's an acme request if strings.HasPrefix(ctx.Path(), challengePath) { - challenge, ok := challengeCache.Get(utils.TrimHostPort(ctx.Host()) + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) + challenge, ok := challengeCache.Get(domain + "/" + strings.TrimPrefix(ctx.Path(), challengePath)) if !ok || challenge == nil { + log.Info().Msgf("HTTP-ACME challenge for '%s' failed: token not found", domain) ctx.String("no challenge for this token", http.StatusNotFound) } + log.Info().Msgf("HTTP-ACME challenge for '%s' succeeded", domain) ctx.String(challenge.(string)) - } else { - ctx.Redirect("https://"+ctx.Host()+ctx.Path(), http.StatusMovedPermanently) + return } + + // it's a normal http request that needs to be redirected + u, err := url.Parse(fmt.Sprintf("https://%s%s%s", domain, portPart, ctx.Path())) + if err != nil { + log.Error().Err(err).Msg("could not craft http to https redirect") + ctx.String("", http.StatusInternalServerError) + } + + newURL := u.String() + log.Debug().Msgf("redirect http to https: %s", newURL) + ctx.Redirect(newURL, http.StatusMovedPermanently) } } diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 707672c..ce1420d 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -47,6 +47,7 @@ func TLSConfig(mainDomainSuffix string, if proto != tlsalpn01.ACMETLS1Protocol { continue } + log.Info().Msgf("Detect ACME-TLS1 challenge for '%s'", domain) challenge, ok := challengeCache.Get(domain) if !ok { From 42d5802b9ba77556dfa529e20caf0442ac6b1fa4 Mon Sep 17 00:00:00 2001 From: deblan Date: Tue, 14 Feb 2023 03:03:00 +0000 Subject: [PATCH 197/320] Allow to define default branches (#125) This try to address #115 Co-authored-by: Simon Vieille Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/125 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: deblan Co-committed-by: deblan --- cmd/flags.go | 7 ++++ cmd/main.go | 7 ++++ go.mod | 3 +- go.sum | 4 +++ integration/main_test.go | 1 + server/certificates/certificates.go | 3 +- server/dns/dns.go | 10 +++--- server/handler/handler.go | 4 ++- server/handler/handler_custom_domain.go | 5 +-- server/handler/handler_sub_domain.go | 46 ++++++++++++++++++++----- server/handler/handler_test.go | 1 + 11 files changed, 73 insertions(+), 18 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index 5bc638b..a71dd35 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -112,6 +112,13 @@ var ( Usage: "specify at which log level should be logged. Possible options: info, warn, error, fatal", EnvVars: []string{"LOG_LEVEL"}, }, + // Default branches to fetch assets from + &cli.StringSliceFlag{ + Name: "pages-branch", + Usage: "define a branch to fetch assets from", + EnvVars: []string{"PAGES_BRANCHES"}, + Value: cli.NewStringSlice("pages"), + }, // ############################ // ### ACME Client Settings ### diff --git a/cmd/main.go b/cmd/main.go index aa00f54..45e151d 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -45,6 +45,7 @@ func Serve(ctx *cli.Context) error { giteaRoot := ctx.String("gitea-root") giteaAPIToken := ctx.String("gitea-api-token") rawDomain := ctx.String("raw-domain") + defaultBranches := ctx.StringSlice("pages-branch") mainDomainSuffix := ctx.String("pages-domain") rawInfoPage := ctx.String("raw-info-page") listeningHost := ctx.String("host") @@ -63,6 +64,10 @@ func Serve(ctx *cli.Context) error { mainDomainSuffix = "." + mainDomainSuffix } + if len(defaultBranches) == 0 { + return fmt.Errorf("no default branches set (PAGES_BRANCHES)") + } + // Init ssl cert database certDB, closeFn, err := openCertDB(ctx) if err != nil { @@ -104,6 +109,7 @@ func Serve(ctx *cli.Context) error { listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, giteaClient, acmeClient, + defaultBranches[0], keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, certDB)) @@ -131,6 +137,7 @@ func Serve(ctx *cli.Context) error { giteaClient, rawInfoPage, BlacklistedPaths, allowedCorsDomains, + defaultBranches, dnsLookupCache, canonicalDomainCache) // Start the ssl listener diff --git a/go.mod b/go.mod index 944e2ad..bfde7f7 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.7.0 github.com/urfave/cli/v2 v2.3.0 + golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb xorm.io/xorm v1.3.2 ) @@ -117,7 +118,7 @@ require ( golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect + golang.org/x/sys v0.1.0 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect diff --git a/go.sum b/go.sum index b5a7568..b10305c 100644 --- a/go.sum +++ b/go.sum @@ -768,6 +768,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= +golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -899,6 +901,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/integration/main_test.go b/integration/main_test.go index a6579fd..a397110 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -39,6 +39,7 @@ func startServer(ctx context.Context) error { setEnvIfNotSet("ACME_API", "https://acme.mock.directory") setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") + setEnvIfNotSet("PAGES_BRANCHES", "pages,main,master") setEnvIfNotSet("PORT", "4430") setEnvIfNotSet("HTTP_PORT", "8880") setEnvIfNotSet("ENABLE_HTTP_SERVER", "true") diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index ce1420d..3ae891a 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -30,6 +30,7 @@ var ErrUserRateLimitExceeded = errors.New("rate limit exceeded: 10 certificates func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, acmeClient *AcmeClient, + firstDefaultBranch string, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, certDB database.CertDB, ) *tls.Config { @@ -68,7 +69,7 @@ func TLSConfig(mainDomainSuffix string, domain = mainDomainSuffix } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, dnsLookupCache) + targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs domain = mainDomainSuffix diff --git a/server/dns/dns.go b/server/dns/dns.go index 2719d4d..c11b278 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -11,9 +11,11 @@ import ( // lookupCacheTimeout specifies the timeout for the DNS lookup cache. var lookupCacheTimeout = 15 * time.Minute +var defaultPagesRepo = "pages" + // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { +func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error @@ -50,10 +52,10 @@ func GetTargetFromDNS(domain, mainDomainSuffix string, dnsLookupCache cache.SetG targetBranch = cnameParts[len(cnameParts)-3] } if targetRepo == "" { - targetRepo = "pages" + targetRepo = defaultPagesRepo } - if targetBranch == "" && targetRepo != "pages" { - targetBranch = "pages" + if targetBranch == "" && targetRepo != defaultPagesRepo { + targetBranch = firstDefaultBranch } // if targetBranch is still empty, the caller must find the default branch return diff --git a/server/handler/handler.go b/server/handler/handler.go index 78301e9..a944c7e 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -17,7 +17,6 @@ const ( headerAccessControlAllowOrigin = "Access-Control-Allow-Origin" headerAccessControlAllowMethods = "Access-Control-Allow-Methods" defaultPagesRepo = "pages" - defaultPagesBranch = "pages" ) // Handler handles a single HTTP request to the web server. @@ -25,6 +24,7 @@ func Handler(mainDomainSuffix, rawDomain string, giteaClient *gitea.Client, rawInfoPage string, blacklistedPaths, allowedCorsDomains []string, + defaultPagesBranches []string, dnsLookupCache, canonicalDomainCache cache.SetGetKey, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { @@ -98,6 +98,7 @@ func Handler(mainDomainSuffix, rawDomain string, log.Debug().Msg("subdomain request detecded") handleSubDomain(log, ctx, giteaClient, mainDomainSuffix, + defaultPagesBranches, trimmedHost, pathElements, canonicalDomainCache) @@ -107,6 +108,7 @@ func Handler(mainDomainSuffix, rawDomain string, mainDomainSuffix, trimmedHost, pathElements, + defaultPagesBranches[0], dnsLookupCache, canonicalDomainCache) } } diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index a541b74..1b85f62 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -18,10 +18,11 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g mainDomainSuffix string, trimmedHost string, pathElements []string, + firstDefaultBranch string, dnsLookupCache, canonicalDomainCache cache.SetGetKey, ) { // Serve pages from custom domains - targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, dnsLookupCache) + targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) if targetOwner == "" { html.ReturnErrorPage(ctx, "could not obtain repo owner from custom domain", @@ -52,7 +53,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g return } else if canonicalDomain != trimmedHost { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, dnsLookupCache) + targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, firstDefaultBranch, dnsLookupCache) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+"/"+targetOpt.TargetPath, http.StatusTemporaryRedirect) return diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 2a75e9f..68f4822 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/rs/zerolog" + "golang.org/x/exp/slices" "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" @@ -17,6 +18,7 @@ import ( func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix string, + defaultPagesBranches []string, trimmedHost string, pathElements []string, canonicalDomainCache cache.SetGetKey, @@ -63,12 +65,21 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite // Check if the first directory is a branch for the defaultPagesRepo // example.codeberg.page/@main/index.html if strings.HasPrefix(pathElements[0], "@") { + targetBranch := pathElements[0][1:] + + // if the default pages branch can be determined exactly, it does not need to be set + if len(defaultPagesBranches) == 1 && slices.Contains(defaultPagesBranches, targetBranch) { + // example.codeberg.org/@pages/... redirects to example.codeberg.org/... + ctx.Redirect("/"+strings.Join(pathElements[1:], "/"), http.StatusTemporaryRedirect) + return + } + log.Debug().Msg("main domain preparations, now trying with specified branch") if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ TryIndexPages: true, TargetOwner: targetOwner, TargetRepo: defaultPagesRepo, - TargetBranch: pathElements[0][1:], + TargetBranch: targetBranch, TargetPath: path.Join(pathElements[1:]...), }, true); works { log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") @@ -81,19 +92,36 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite return } - // Check if the first directory is a repo with a defaultPagesRepo branch - // example.codeberg.page/myrepo/index.html - // example.codeberg.page/pages/... is not allowed here. - log.Debug().Msg("main domain preparations, now trying with specified repo") - if pathElements[0] != defaultPagesRepo { + for _, defaultPagesBranch := range defaultPagesBranches { + // Check if the first directory is a repo with a default pages branch + // example.codeberg.page/myrepo/index.html + // example.codeberg.page/{PAGES_BRANCHE}/... is not allowed here. + log.Debug().Msg("main domain preparations, now trying with specified repo") + if pathElements[0] != defaultPagesBranch { + if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ + TryIndexPages: true, + TargetOwner: targetOwner, + TargetRepo: pathElements[0], + TargetBranch: defaultPagesBranch, + TargetPath: path.Join(pathElements[1:]...), + }, false); works { + log.Debug().Msg("tryBranch, now trying upstream 5") + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + return + } + } + + // Try to use the defaultPagesRepo on an default pages branch + // example.codeberg.page/index.html + log.Debug().Msg("main domain preparations, now trying with default repo") if targetOpt, works := tryBranch(log, ctx, giteaClient, &upstream.Options{ TryIndexPages: true, TargetOwner: targetOwner, - TargetRepo: pathElements[0], + TargetRepo: defaultPagesRepo, TargetBranch: defaultPagesBranch, - TargetPath: path.Join(pathElements[1:]...), + TargetPath: path.Join(pathElements...), }, false); works { - log.Debug().Msg("tryBranch, now trying upstream 5") + log.Debug().Msg("tryBranch, now trying upstream 6") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) return } diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index 626564a..ed063b2 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -18,6 +18,7 @@ func TestHandlerPerformance(t *testing.T) { "https://docs.codeberg.org/pages/raw-content/", []string{"/.well-known/acme-challenge/"}, []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, + []string{"pages"}, cache.NewKeyValueCache(), cache.NewKeyValueCache(), ) From c9050e5722120f03fc5c8e632aa785d790f2b513 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sat, 11 Mar 2023 05:07:17 +0000 Subject: [PATCH 198/320] Handle Relative Symlinks (#205) enhance #114 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/205 --- integration/get_test.go | 13 ++++++++++++- integration/main_test.go | 2 +- server/certificates/acme_client.go | 2 +- server/database/xorm.go | 2 +- server/gitea/client.go | 6 +++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/integration/get_test.go b/integration/get_test.go index 3a7190a..c0a3a47 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -64,7 +64,7 @@ func TestGetContent(t *testing.T) { assert.True(t, getSize(resp.Body) > 100) assert.Len(t, resp.Header.Get("ETag"), 44) - // TODO: test get of non cachable content (content size > fileCacheSizeLimit) + // TODO: test get of non cacheable content (content size > fileCacheSizeLimit) } func TestCustomDomain(t *testing.T) { @@ -154,6 +154,7 @@ func TestGetNotFound(t *testing.T) { func TestFollowSymlink(t *testing.T) { log.Printf("=== TestFollowSymlink ===\n") + // file symlink resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/tests_for_pages-server/@main/link") assert.NoError(t, err) if !assert.NotNil(t, resp) { @@ -165,6 +166,16 @@ func TestFollowSymlink(t *testing.T) { body := getBytes(resp.Body) assert.EqualValues(t, 4, len(body)) assert.EqualValues(t, "abc\n", string(body)) + + // relative file links (../index.html file in this case) + resp, err = getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/tests_for_pages-server/@main/dir_aim/some/") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "an index\n", string(getBytes(resp.Body))) } func TestLFSSupport(t *testing.T) { diff --git a/integration/main_test.go b/integration/main_test.go index a397110..6566f78 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -23,7 +23,7 @@ func TestMain(m *testing.M) { } defer func() { serverCancel() - log.Println("=== TestMain: Server STOPED ===") + log.Println("=== TestMain: Server STOPPED ===") }() time.Sleep(10 * time.Second) diff --git a/server/certificates/acme_client.go b/server/certificates/acme_client.go index 7737396..ba83e50 100644 --- a/server/certificates/acme_client.go +++ b/server/certificates/acme_client.go @@ -89,7 +89,7 @@ func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeClientRequestLimit: equalizer.NewTokenBucket(5, 1*time.Second), // rate limit is 5 / hour https://letsencrypt.org/docs/failed-validation-limit/ acmeClientFailLimit: equalizer.NewTokenBucket(5, 1*time.Hour), - // checkUserLimit() use this to rate als per user + // checkUserLimit() use this to rate also per user acmeClientCertificateLimitPerUser: map[string]*equalizer.TokenBucket{}, }, nil } diff --git a/server/database/xorm.go b/server/database/xorm.go index 4b43cbb..fb1dc17 100644 --- a/server/database/xorm.go +++ b/server/database/xorm.go @@ -37,7 +37,7 @@ func NewXormDB(dbType, dbConn string) (CertDB, error) { } if err := e.Sync2(new(Cert)); err != nil { - return nil, fmt.Errorf("cound not sync db model :%w", err) + return nil, fmt.Errorf("could not sync db model :%w", err) } return &xDB{ diff --git a/server/gitea/client.go b/server/gitea/client.go index 51647ba..7a2bf63 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -112,7 +112,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str if cache, ok := client.responseCache.Get(cacheKey); ok { cache := cache.(FileResponse) cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) - // TODO: check against some timestamp missmatch?!? + // TODO: check against some timestamp mismatch?!? if cache.Exists { if cache.IsSymlink { linkDest := string(cache.Body) @@ -145,6 +145,10 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str } linkDest := strings.TrimSpace(string(linkDestBytes)) + // handle relative links + // we first remove the link from the path, and make a relative join (resolve parent paths like "/../" too) + linkDest = path.Join(path.Dir(resource), linkDest) + // we store symlink not content to reduce duplicates in cache if err := client.responseCache.Set(cacheKey, FileResponse{ Exists: true, From 26d59b71f049d459b663ec885156bfb8b89aa0c9 Mon Sep 17 00:00:00 2001 From: Crystal Date: Mon, 20 Mar 2023 22:52:42 +0000 Subject: [PATCH 199/320] Fix typo in integration test log (#210) I forgot to update the name of this function in the CI log so it looks like it's running the same test twice even though it's not. Co-authored-by: crystal Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/210 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Crystal Co-committed-by: Crystal --- integration/get_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/get_test.go b/integration/get_test.go index c0a3a47..9d97390 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -124,7 +124,7 @@ func TestRawCustomDomain(t *testing.T) { } func TestRawIndex(t *testing.T) { - log.Println("=== TestRawCustomDomain ===") + log.Println("=== TestRawIndex ===") // test raw domain response for index.html resp, err := getTestHTTPSClient().Get("https://raw.localhost.mock.directory:4430/cb_pages_tests/raw-test/@branch-test/index.html") // need cb_pages_tests fork assert.NoError(t, err) From c40dddf471277260ee3ca95c5701cf723c1e24cb Mon Sep 17 00:00:00 2001 From: Crystal Date: Mon, 20 Mar 2023 22:57:26 +0000 Subject: [PATCH 200/320] Fix certificate renewal (#209) A database bug in xorm.go prevents the pages-server from saving a renewed certificate for a domain that already has one in the database. Co-authored-by: crystal Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/209 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Crystal Co-committed-by: Crystal --- server/database/xorm.go | 2 +- server/database/xorm_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/database/xorm.go b/server/database/xorm.go index fb1dc17..217b6d1 100644 --- a/server/database/xorm.go +++ b/server/database/xorm.go @@ -64,7 +64,7 @@ func (x xDB) Put(domain string, cert *certificate.Resource) error { } defer sess.Close() - if exist, _ := sess.ID(c.Domain).Exist(); exist { + if exist, _ := sess.ID(c.Domain).Exist(new(Cert)); exist { if _, err := sess.ID(c.Domain).Update(c); err != nil { return err } diff --git a/server/database/xorm_test.go b/server/database/xorm_test.go index 9c032ee..50d8a7f 100644 --- a/server/database/xorm_test.go +++ b/server/database/xorm_test.go @@ -37,7 +37,7 @@ func TestSanitizeWildcardCerts(t *testing.T) { })) // update existing cert - assert.Error(t, certDB.Put(".wildcard.de", &certificate.Resource{ + assert.NoError(t, certDB.Put(".wildcard.de", &certificate.Resource{ Domain: "*.wildcard.de", Certificate: localhost_mock_directory_certificate, })) From 98d7a771be9783a333a3f9abd8d6e08d1ad4a46f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 21 Mar 2023 01:53:07 +0100 Subject: [PATCH 201/320] Readme.md: add link to chat & main repo --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 5707d65..7e1b2c5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Codeberg Pages +[![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/) +[![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server) + + + + Gitea lacks the ability to host static pages from Git. The Codeberg Pages Server addresses this lack by implementing a standalone service that connects to Gitea via API. @@ -8,6 +14,9 @@ It is suitable to be deployed by other Gitea instances, too, to offer static pag **End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview) and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/). + + Get It On Codeberg + ## Quickstart This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. @@ -29,6 +38,10 @@ record that points to your repo (just like the CNAME record): Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. +## Chat for admins & devs + +[matrix: #gitea-pages-server:obermui.de](https://matrix.to/#/#gitea-pages-server:obermui.de) + ## Deployment **Warning: Some Caveats Apply** From 970c13cf5c006dfa1011ba9e6721baafa3f1ad55 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 21 Mar 2023 02:32:25 +0100 Subject: [PATCH 202/320] Readme.md: use matrix.org for room alias --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7e1b2c5..b694e29 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/) [![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server) - - + + Gitea lacks the ability to host static pages from Git. @@ -40,7 +40,7 @@ Certificates are generated, updated and cleaned up automatically via Let's Encry ## Chat for admins & devs -[matrix: #gitea-pages-server:obermui.de](https://matrix.to/#/#gitea-pages-server:obermui.de) +[matrix: #gitea-pages-server:matrix.org](https://matrix.to/#/#gitea-pages-server:matrix.org) ## Deployment From 974229681f4cc7f1ed31df9b05eabef2df013809 Mon Sep 17 00:00:00 2001 From: video-prize-ranch Date: Thu, 30 Mar 2023 21:36:31 +0000 Subject: [PATCH 203/320] Initial redirects implementation (#148) Adds basic support for `_redirects` files. It supports a subset of what IPFS supports: https://docs.ipfs.tech/how-to/websites-on-ipfs/redirects-and-custom-404s/ Example: ``` /redirect https://example.com/ 301 /another-redirect /page 301 /302 https://example.com/ 302 /app/* /index.html 200 /articles/* /posts/:splat 301 ``` 301 redirect: https://video-prize-ranch.localhost.mock.directory:4430/redirect SPA rewrite: https://video-prize-ranch.localhost.mock.directory:4430/app/path/path Catch-all with splat: https://video-prize-ranch.localhost.mock.directory:4430/articles/path/path Closes #46 Co-authored-by: video-prize-ranch Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/148 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: video-prize-ranch Co-committed-by: video-prize-ranch --- FEATURES.md | 45 +++++++++ cmd/main.go | 4 +- integration/get_test.go | 40 ++++++++ server/handler/handler.go | 8 +- server/handler/handler_custom_domain.go | 4 +- server/handler/handler_raw_domain.go | 6 +- server/handler/handler_sub_domain.go | 12 +-- server/handler/handler_test.go | 1 + server/handler/try.go | 3 +- server/upstream/redirects.go | 117 ++++++++++++++++++++++++ server/upstream/upstream.go | 16 +++- 11 files changed, 235 insertions(+), 21 deletions(-) create mode 100644 FEATURES.md create mode 100644 server/upstream/redirects.go diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 0000000..7560a1d --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,45 @@ +# Features + +## Custom domains + +... + +## Redirects + +Redirects can be created with a `_redirects` file with the following format: + +``` +# Comment +from to [status] +``` + +* Lines starting with `#` are ignored +* `from` - the path to redirect from (Note: repository and branch names are removed from request URLs) +* `to` - the path or URL to redirect to +* `status` - status code to use when redirecting (default 301) + +### Status codes + +* `200` - returns content from specified path (no external URLs) without changing the URL (rewrite) +* `301` - Moved Permanently (Permanent redirect) +* `302` - Found (Temporary redirect) + +### Examples + +#### SPA (single-page application) rewrite + +Redirects all paths to `/index.html` for single-page apps. + +``` +/* /index.html 200 +``` + +#### Splats + +Redirects every path under `/articles` to `/posts` while keeping the path. + +``` +/articles/* /posts/:splat 302 +``` + +Example: `/articles/2022/10/12/post-1/` -> `/posts/2022/10/12/post-1/` diff --git a/cmd/main.go b/cmd/main.go index 45e151d..84915c9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -81,6 +81,8 @@ func Serve(ctx *cli.Context) error { canonicalDomainCache := cache.NewKeyValueCache() // dnsLookupCache stores DNS lookups for custom domains dnsLookupCache := cache.NewKeyValueCache() + // redirectsCache stores redirects in _redirects files + redirectsCache := cache.NewKeyValueCache() // clientResponseCache stores responses from the Gitea server clientResponseCache := cache.NewKeyValueCache() @@ -138,7 +140,7 @@ func Serve(ctx *cli.Context) error { rawInfoPage, BlacklistedPaths, allowedCorsDomains, defaultBranches, - dnsLookupCache, canonicalDomainCache) + dnsLookupCache, canonicalDomainCache, redirectsCache) // Start the ssl listener log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) diff --git a/integration/get_test.go b/integration/get_test.go index 9d97390..cfb7188 100644 --- a/integration/get_test.go +++ b/integration/get_test.go @@ -151,6 +151,46 @@ func TestGetNotFound(t *testing.T) { assert.EqualValues(t, 37, getSize(resp.Body)) } +func TestRedirect(t *testing.T) { + log.Println("=== TestRedirect ===") + // test redirects + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/some_redirects/redirect") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusMovedPermanently, resp.StatusCode) + assert.EqualValues(t, "https://example.com/", resp.Header.Get("Location")) +} + +func TestSPARedirect(t *testing.T) { + log.Println("=== TestSPARedirect ===") + // test SPA redirects + url := "https://cb_pages_tests.localhost.mock.directory:4430/some_redirects/app/aqdjw" + resp, err := getTestHTTPSClient().Get(url) + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusOK, resp.StatusCode) + assert.EqualValues(t, url, resp.Request.URL.String()) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + assert.EqualValues(t, "258", resp.Header.Get("Content-Length")) + assert.EqualValues(t, 258, getSize(resp.Body)) +} + +func TestSplatRedirect(t *testing.T) { + log.Println("=== TestSplatRedirect ===") + // test splat redirects + resp, err := getTestHTTPSClient().Get("https://cb_pages_tests.localhost.mock.directory:4430/some_redirects/articles/qfopefe") + assert.NoError(t, err) + if !assert.NotNil(t, resp) { + t.FailNow() + } + assert.EqualValues(t, http.StatusMovedPermanently, resp.StatusCode) + assert.EqualValues(t, "/posts/qfopefe", resp.Header.Get("Location")) +} + func TestFollowSymlink(t *testing.T) { log.Printf("=== TestFollowSymlink ===\n") diff --git a/server/handler/handler.go b/server/handler/handler.go index a944c7e..7edcf95 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -25,7 +25,7 @@ func Handler(mainDomainSuffix, rawDomain string, rawInfoPage string, blacklistedPaths, allowedCorsDomains []string, defaultPagesBranches []string, - dnsLookupCache, canonicalDomainCache cache.SetGetKey, + dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() @@ -93,7 +93,7 @@ func Handler(mainDomainSuffix, rawDomain string, mainDomainSuffix, rawInfoPage, trimmedHost, pathElements, - canonicalDomainCache) + canonicalDomainCache, redirectsCache) } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { log.Debug().Msg("subdomain request detecded") handleSubDomain(log, ctx, giteaClient, @@ -101,7 +101,7 @@ func Handler(mainDomainSuffix, rawDomain string, defaultPagesBranches, trimmedHost, pathElements, - canonicalDomainCache) + canonicalDomainCache, redirectsCache) } else { log.Debug().Msg("custom domain request detecded") handleCustomDomain(log, ctx, giteaClient, @@ -109,7 +109,7 @@ func Handler(mainDomainSuffix, rawDomain string, trimmedHost, pathElements, defaultPagesBranches[0], - dnsLookupCache, canonicalDomainCache) + dnsLookupCache, canonicalDomainCache, redirectsCache) } } } diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 1b85f62..8742be4 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -19,7 +19,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g trimmedHost string, pathElements []string, firstDefaultBranch string, - dnsLookupCache, canonicalDomainCache cache.SetGetKey, + dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey, ) { // Serve pages from custom domains targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) @@ -64,7 +64,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g } log.Debug().Msg("tryBranch, now trying upstream 7") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go index 5e974da..aa41c52 100644 --- a/server/handler/handler_raw_domain.go +++ b/server/handler/handler_raw_domain.go @@ -19,7 +19,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie mainDomainSuffix, rawInfoPage string, trimmedHost string, pathElements []string, - canonicalDomainCache cache.SetGetKey, + canonicalDomainCache, redirectsCache cache.SetGetKey, ) { // Serve raw content from RawDomain log.Debug().Msg("raw domain") @@ -41,7 +41,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie TargetPath: path.Join(pathElements[3:]...), }, true); works { log.Trace().Msg("tryUpstream: serve raw domain with specified branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } log.Debug().Msg("missing branch info") @@ -58,7 +58,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie TargetPath: path.Join(pathElements[2:]...), }, true); works { log.Trace().Msg("tryUpstream: serve raw domain with default branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage(ctx, fmt.Sprintf("raw domain could not find repo '%s/%s' or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo), diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 68f4822..8731bec 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -21,7 +21,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite defaultPagesBranches []string, trimmedHost string, pathElements []string, - canonicalDomainCache cache.SetGetKey, + canonicalDomainCache, redirectsCache cache.SetGetKey, ) { // Serve pages from subdomains of MainDomainSuffix log.Debug().Msg("main domain suffix") @@ -53,7 +53,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[2:]...), }, true); works { log.Trace().Msg("tryUpstream: serve with specified repo and branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage(ctx, fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), @@ -83,7 +83,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[1:]...), }, true); works { log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage(ctx, fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), @@ -106,7 +106,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[1:]...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 5") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } } @@ -122,7 +122,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } } @@ -137,7 +137,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache) + tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index ed063b2..de705ec 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -21,6 +21,7 @@ func TestHandlerPerformance(t *testing.T) { []string{"pages"}, cache.NewKeyValueCache(), cache.NewKeyValueCache(), + cache.NewKeyValueCache(), ) testCase := func(uri string, status int) { diff --git a/server/handler/try.go b/server/handler/try.go index 5c65138..6cfe08e 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -18,6 +18,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix, trimmedHost string, options *upstream.Options, canonicalDomainCache cache.SetGetKey, + redirectsCache cache.SetGetKey, ) { // check if a canonical domain exists on a request on MainDomain if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw { @@ -39,7 +40,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, options.Host = trimmedHost // Try to request the file from the Gitea API - if !options.Upstream(ctx, giteaClient) { + if !options.Upstream(ctx, giteaClient, redirectsCache) { html.ReturnErrorPage(ctx, "", ctx.StatusCode) } } diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go new file mode 100644 index 0000000..ab6c971 --- /dev/null +++ b/server/upstream/redirects.go @@ -0,0 +1,117 @@ +package upstream + +import ( + "strconv" + "strings" + "time" + + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" + "codeberg.org/codeberg/pages/server/gitea" + "github.com/rs/zerolog/log" +) + +type Redirect struct { + From string + To string + StatusCode int +} + +// redirectsCacheTimeout specifies the timeout for the redirects cache. +var redirectsCacheTimeout = 10 * time.Minute + +const redirectsConfig = "_redirects" + +// getRedirects returns redirects specified in the _redirects file. +func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.SetGetKey) []Redirect { + var redirects []Redirect + cacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch + + // Check for cached redirects + if cachedValue, ok := redirectsCache.Get(cacheKey); ok { + redirects = cachedValue.([]Redirect) + } else { + // Get _redirects file and parse + body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig) + if err == nil { + for _, line := range strings.Split(string(body), "\n") { + redirectArr := strings.Fields(line) + + // Ignore comments and invalid lines + if strings.HasPrefix(line, "#") || len(redirectArr) < 2 { + continue + } + + // Get redirect status code + statusCode := 301 + if len(redirectArr) == 3 { + statusCode, err = strconv.Atoi(redirectArr[2]) + if err != nil { + log.Info().Err(err).Msgf("could not read %s of %s/%s", redirectsConfig, o.TargetOwner, o.TargetRepo) + } + } + + redirects = append(redirects, Redirect{ + From: redirectArr[0], + To: redirectArr[1], + StatusCode: statusCode, + }) + } + } + _ = redirectsCache.Set(cacheKey, redirects, redirectsCacheTimeout) + } + return redirects +} + +func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client, redirects []Redirect, redirectsCache cache.SetGetKey) (final bool) { + if len(redirects) > 0 { + for _, redirect := range redirects { + reqUrl := ctx.Req.RequestURI + // remove repo and branch from request url + reqUrl = strings.TrimPrefix(reqUrl, "/"+o.TargetRepo) + reqUrl = strings.TrimPrefix(reqUrl, "/@"+o.TargetBranch) + + // check if from url matches request url + if strings.TrimSuffix(redirect.From, "/") == strings.TrimSuffix(reqUrl, "/") { + // do rewrite if status code is 200 + if redirect.StatusCode == 200 { + o.TargetPath = redirect.To + o.Upstream(ctx, giteaClient, redirectsCache) + return true + } else { + ctx.Redirect(redirect.To, redirect.StatusCode) + return true + } + } + + // handle wildcard redirects + trimmedFromUrl := strings.TrimSuffix(redirect.From, "/*") + if strings.HasSuffix(redirect.From, "/*") && strings.HasPrefix(reqUrl, trimmedFromUrl) { + if strings.Contains(redirect.To, ":splat") { + splatUrl := strings.ReplaceAll(redirect.To, ":splat", strings.TrimPrefix(reqUrl, trimmedFromUrl)) + // do rewrite if status code is 200 + if redirect.StatusCode == 200 { + o.TargetPath = splatUrl + o.Upstream(ctx, giteaClient, redirectsCache) + return true + } else { + ctx.Redirect(splatUrl, redirect.StatusCode) + return true + } + } else { + // do rewrite if status code is 200 + if redirect.StatusCode == 200 { + o.TargetPath = redirect.To + o.Upstream(ctx, giteaClient, redirectsCache) + return true + } else { + ctx.Redirect(redirect.To, redirect.StatusCode) + return true + } + } + } + } + } + + return false +} diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 3845969..f97c6ae 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -11,6 +11,7 @@ import ( "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/html" + "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/gitea" ) @@ -52,7 +53,7 @@ type Options struct { } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (final bool) { +func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) (final bool) { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() if o.TargetOwner == "" || o.TargetRepo == "" { @@ -103,6 +104,12 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin // Handle not found error if err != nil && errors.Is(err, gitea.ErrorNotFound) { + // Get and match redirects + redirects := o.getRedirects(giteaClient, redirectsCache) + if o.matchRedirects(ctx, giteaClient, redirects, redirectsCache) { + return true + } + if o.TryIndexPages { // copy the o struct & try if an index page exists optionsForIndexPages := *o @@ -110,7 +117,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin optionsForIndexPages.appendTrailingSlash = true for _, indexPage := range upstreamIndexPages { optionsForIndexPages.TargetPath = strings.TrimSuffix(o.TargetPath, "/") + "/" + indexPage - if optionsForIndexPages.Upstream(ctx, giteaClient) { + if optionsForIndexPages.Upstream(ctx, giteaClient, redirectsCache) { return true } } @@ -118,7 +125,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin optionsForIndexPages.appendTrailingSlash = false optionsForIndexPages.redirectIfExists = strings.TrimSuffix(ctx.Path(), "/") + ".html" optionsForIndexPages.TargetPath = o.TargetPath + ".html" - if optionsForIndexPages.Upstream(ctx, giteaClient) { + if optionsForIndexPages.Upstream(ctx, giteaClient, redirectsCache) { return true } } @@ -131,11 +138,12 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client) (fin optionsForNotFoundPages.appendTrailingSlash = false for _, notFoundPage := range upstreamNotFoundPages { optionsForNotFoundPages.TargetPath = "/" + notFoundPage - if optionsForNotFoundPages.Upstream(ctx, giteaClient) { + if optionsForNotFoundPages.Upstream(ctx, giteaClient, redirectsCache) { return true } } } + return false } From 7f318f89a6696e9a39d510bb8568ba74c4c0043c Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 16 Jul 2023 22:34:46 +0000 Subject: [PATCH 204/320] Fix escaped error message (#230) - This specific message will [already be generated](https://codeberg.org/Codeberg/pages-server/src/commit/974229681f4cc7f1ed31df9b05eabef2df01380/html/error.go#L44) when `http.StatusMisdirectedRequest` is set as status with [an empty message](https://codeberg.org/Codeberg/pages-server/src/commit/974229681f4cc7f1ed31df9b05eabef2df013809/html/error.go#L25-L28). - Resolves https://codeberg.org/Codeberg/pages-server/issues/228 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/230 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: Gusted Co-committed-by: Gusted --- server/handler/handler_custom_domain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 8742be4..299d7cf 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -49,7 +49,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g }, canonicalLink); works { canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) if !valid { - html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) + html.ReturnErrorPage(ctx, "", http.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHost { // only redirect if the target is also a codeberg page! From d720d25e42eb5f1e63462a6665afdd0bebd364ae Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 17 Jul 2023 19:44:58 +0000 Subject: [PATCH 205/320] Use http.NoBody as per linter (#231) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/231 Reviewed-by: Gusted --- server/handler/handler_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index de705ec..6521633 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -1,6 +1,7 @@ package handler import ( + "net/http" "net/http/httptest" "testing" "time" @@ -26,7 +27,7 @@ func TestHandlerPerformance(t *testing.T) { testCase := func(uri string, status int) { t.Run(uri, func(t *testing.T) { - req := httptest.NewRequest("GET", uri, nil) + req := httptest.NewRequest("GET", uri, http.NoBody) w := httptest.NewRecorder() log.Printf("Start: %v\n", time.Now()) From 56d3e291c4579c484b173fbcee07b3334c910c23 Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 27 Aug 2023 10:13:15 +0200 Subject: [PATCH 206/320] Security Fix: clean paths correctly to avoid circumvention of BlacklistedPaths --- server/context/context.go | 6 ++-- server/utils/utils.go | 14 ++++++++++ server/utils/utils_test.go | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/server/context/context.go b/server/context/context.go index 481fee2..6650164 100644 --- a/server/context/context.go +++ b/server/context/context.go @@ -48,11 +48,9 @@ func (c *Context) Redirect(uri string, statusCode int) { http.Redirect(c.RespWriter, c.Req, uri, statusCode) } -// Path returns requested path. -// -// The returned bytes are valid until your request handler returns. +// Path returns the cleaned requested path. func (c *Context) Path() string { - return c.Req.URL.Path + return utils.CleanPath(c.Req.URL.Path) } func (c *Context) Host() string { diff --git a/server/utils/utils.go b/server/utils/utils.go index 30f948d..91ed359 100644 --- a/server/utils/utils.go +++ b/server/utils/utils.go @@ -1,6 +1,8 @@ package utils import ( + "net/url" + "path" "strings" ) @@ -11,3 +13,15 @@ func TrimHostPort(host string) string { } return host } + +func CleanPath(uriPath string) string { + unescapedPath, _ := url.PathUnescape(uriPath) + cleanedPath := path.Join("/", unescapedPath) + + // If the path refers to a directory, add a trailing slash. + if !strings.HasSuffix(cleanedPath, "/") && (strings.HasSuffix(unescapedPath, "/") || strings.HasSuffix(unescapedPath, "/.") || strings.HasSuffix(unescapedPath, "/..")) { + cleanedPath += "/" + } + + return cleanedPath +} diff --git a/server/utils/utils_test.go b/server/utils/utils_test.go index 2532392..b8fcea9 100644 --- a/server/utils/utils_test.go +++ b/server/utils/utils_test.go @@ -11,3 +11,59 @@ func TestTrimHostPort(t *testing.T) { assert.EqualValues(t, "", TrimHostPort(":")) assert.EqualValues(t, "example.com", TrimHostPort("example.com:80")) } + +// TestCleanPath is mostly copied from fasthttp, to keep the behaviour we had before migrating away from it. +// Source (MIT licensed): https://github.com/valyala/fasthttp/blob/v1.48.0/uri_test.go#L154 +// Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors +func TestCleanPath(t *testing.T) { + // double slash + testURIPathNormalize(t, "/aa//bb", "/aa/bb") + + // triple slash + testURIPathNormalize(t, "/x///y/", "/x/y/") + + // multi slashes + testURIPathNormalize(t, "/abc//de///fg////", "/abc/de/fg/") + + // encoded slashes + testURIPathNormalize(t, "/xxxx%2fyyy%2f%2F%2F", "/xxxx/yyy/") + + // dotdot + testURIPathNormalize(t, "/aaa/..", "/") + + // dotdot with trailing slash + testURIPathNormalize(t, "/xxx/yyy/../", "/xxx/") + + // multi dotdots + testURIPathNormalize(t, "/aaa/bbb/ccc/../../ddd", "/aaa/ddd") + + // dotdots separated by other data + testURIPathNormalize(t, "/a/b/../c/d/../e/..", "/a/c/") + + // too many dotdots + testURIPathNormalize(t, "/aaa/../../../../xxx", "/xxx") + testURIPathNormalize(t, "/../../../../../..", "/") + testURIPathNormalize(t, "/../../../../../../", "/") + + // encoded dotdots + testURIPathNormalize(t, "/aaa%2Fbbb%2F%2E.%2Fxxx", "/aaa/xxx") + + // double slash with dotdots + testURIPathNormalize(t, "/aaa////..//b", "/b") + + // fake dotdot + testURIPathNormalize(t, "/aaa/..bbb/ccc/..", "/aaa/..bbb/") + + // single dot + testURIPathNormalize(t, "/a/./b/././c/./d.html", "/a/b/c/d.html") + testURIPathNormalize(t, "./foo/", "/foo/") + testURIPathNormalize(t, "./../.././../../aaa/bbb/../../../././../", "/") + testURIPathNormalize(t, "./a/./.././../b/./foo.html", "/b/foo.html") +} + +func testURIPathNormalize(t *testing.T, requestURI, expectedPath string) { + cleanedPath := CleanPath(requestURI) + if cleanedPath != expectedPath { + t.Fatalf("Unexpected path %q. Expected %q. requestURI=%q", cleanedPath, expectedPath, requestURI) + } +} From ff3cd1ba353f42fefe7ef1c1ba907e9ea2c8bc5e Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Sun, 27 Aug 2023 11:10:55 +0200 Subject: [PATCH 207/320] Fix CI pipeline (replace "pipeline" with "steps") --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 5b07053..7de8ac8 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,4 +1,4 @@ -pipeline: +steps: # use vendor to cache dependencies vendor: image: golang:1.20 From b6103c6a1b5b2c0802a75ccead7dc3e3f080da12 Mon Sep 17 00:00:00 2001 From: thepaperpilot Date: Sun, 17 Sep 2023 16:45:20 +0000 Subject: [PATCH 208/320] Chore: Fix env var description (#251) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/251 Reviewed-by: crapStone Co-authored-by: thepaperpilot Co-committed-by: thepaperpilot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b694e29..1cb6967 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `RAW_INFO_PAGE` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. - `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. -- `ACME_EMAIL` (default: `noreply@example.email`): Set this to "true" to accept the Terms of Service of your ACME provider. +- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. From a8272f0ce9fab991229aad53f512e6aab9c771d2 Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 15 Nov 2023 01:49:29 +0000 Subject: [PATCH 209/320] Don't send server version to client (#254) closes #247 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/254 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- server/handler/handler.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/handler/handler.go b/server/handler/handler.go index 7edcf95..fbdafd7 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -10,7 +10,6 @@ import ( "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/gitea" - "codeberg.org/codeberg/pages/server/version" ) const ( @@ -31,7 +30,7 @@ func Handler(mainDomainSuffix, rawDomain string, log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() ctx := context.New(w, req) - ctx.RespWriter.Header().Set("Server", "CodebergPages/"+version.Version) + ctx.RespWriter.Header().Set("Server", "pages-server") // Force new default from specification (since November 2020) - see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#strict-origin-when-cross-origin ctx.RespWriter.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") @@ -88,14 +87,14 @@ func Handler(mainDomainSuffix, rawDomain string, pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { - log.Debug().Msg("raw domain request detecded") + log.Debug().Msg("raw domain request detected") handleRaw(log, ctx, giteaClient, mainDomainSuffix, rawInfoPage, trimmedHost, pathElements, canonicalDomainCache, redirectsCache) } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { - log.Debug().Msg("subdomain request detecded") + log.Debug().Msg("subdomain request detected") handleSubDomain(log, ctx, giteaClient, mainDomainSuffix, defaultPagesBranches, @@ -103,7 +102,7 @@ func Handler(mainDomainSuffix, rawDomain string, pathElements, canonicalDomainCache, redirectsCache) } else { - log.Debug().Msg("custom domain request detecded") + log.Debug().Msg("custom domain request detected") handleCustomDomain(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, From be92f30e6434131fda8a78f31ef6e10ea7e9dfdd Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 15 Nov 2023 10:27:27 +0000 Subject: [PATCH 210/320] Update gitea sdk to e23e8aa3004f (#257) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/257 Reviewed-by: crapStone Co-authored-by: 6543 <6543@obermui.de> Co-committed-by: 6543 <6543@obermui.de> --- go.mod | 2 +- go.sum | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index bfde7f7..72eb289 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module codeberg.org/codeberg/pages go 1.20 require ( - code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa + code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/go-acme/lego/v4 v4.5.3 github.com/go-sql-driver/mysql v1.6.0 diff --git a/go.sum b/go.sum index b10305c..f78ae18 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa h1:OVwgYrY6vr6gWZvgnmevFhtL0GVA4HKaFOhD+joPoNk= -code.gitea.io/sdk/gitea v0.15.1-0.20220729105105-cc14c63cccfa/go.mod h1:aRmrQC3CAHdJAU1LQt0C9zqzqI8tUB/5oQtNE746aYE= +code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f h1:nMmwDgUIAWj9XQjzHz5unC3ZMfhhwHRk6rnuwLzdu1o= +code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= @@ -249,8 +249,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -789,8 +789,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -899,8 +899,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -964,14 +962,13 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= From 1e1c67be93f59af3fad68cdc8faad72aac8a742b Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 15 Nov 2023 15:25:14 +0000 Subject: [PATCH 211/320] let gitea client send user-agent with version (#258) closes #255 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/258 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- server/gitea/client.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/server/gitea/client.go b/server/gitea/client.go index 7a2bf63..f3bda54 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -17,12 +17,13 @@ import ( "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/version" ) var ErrorNotFound = errors.New("not found") const ( - // cache key prefixe + // cache key prefixes branchTimestampCacheKeyPrefix = "branchTime" defaultBranchCacheKeyPrefix = "defaultBranch" rawContentCacheKeyPrefix = "rawContent" @@ -76,7 +77,13 @@ func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, follo defaultMimeType = "application/octet-stream" } - sdk, err := gitea.NewClient(giteaRoot, gitea.SetHTTPClient(&stdClient), gitea.SetToken(giteaAPIToken)) + sdk, err := gitea.NewClient( + giteaRoot, + gitea.SetHTTPClient(&stdClient), + gitea.SetToken(giteaAPIToken), + gitea.SetUserAgent("pages-server/"+version.Version), + ) + return &Client{ sdkClient: sdk, responseCache: respCache, @@ -172,7 +179,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str return reader, resp.Response.Header, resp.StatusCode, err } - // now we write to cache and respond at the sime time + // now we write to cache and respond at the same time fileResp := FileResponse{ Exists: true, ETag: resp.Header.Get(ETagHeader), @@ -278,11 +285,11 @@ func shouldRespBeSavedToCache(resp *http.Response) bool { return false } - contentLeng, err := strconv.ParseInt(contentLengthRaw, 10, 64) + contentLength, err := strconv.ParseInt(contentLengthRaw, 10, 64) if err != nil { log.Error().Err(err).Msg("could not parse content length") } // if content to big or could not be determined we not cache it - return contentLeng > 0 && contentLeng < fileCacheSizeLimit + return contentLength > 0 && contentLength < fileCacheSizeLimit } From ea68a82cd22a8a8c1f265260af22b9406f13e3a9 Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 15 Nov 2023 15:25:56 +0000 Subject: [PATCH 212/320] new maintainer (#256) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/256 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1cb6967..9ec3aab 100644 --- a/README.md +++ b/README.md @@ -91,9 +91,13 @@ Since we are working nicely in a team, it might be hard at times to get started (still check out the issues, we always aim to have some things to get you started). If you have any questions, want to work on a feature or could imagine collaborating with us for some time, -feel free to ping us in an issue or in a general Matrix chatgroup. +feel free to ping us in an issue or in a general [Matrix chat room](#chat-for-admins--devs). -You can also contact the maintainers of this project: +You can also contact the maintainer(s) of this project: + +- [crapStone](https://codeberg.org/crapStone) [(Matrix)](https://matrix.to/#/@crapstone:obermui.de) + +Previous maintainers: - [momar](https://codeberg.org/momar) [(Matrix)](https://matrix.to/#/@moritz:wuks.space) - [6543](https://codeberg.org/6543) [(Matrix)](https://matrix.to/#/@marddl:obermui.de) From 7f0a4e5ca999189527bf0739467787fe74a51f8a Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 15 Nov 2023 17:59:04 +0000 Subject: [PATCH 213/320] small cleanup (#259) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/259 Co-authored-by: crapStone Co-committed-by: crapStone --- FEATURES.md | 14 +++++++--- README.md | 27 +++++++++---------- .../haproxy-sni}/.gitignore | 0 .../haproxy-sni}/README.md | 0 .../haproxy-sni}/dhparam.pem | 0 .../haproxy-sni}/docker-compose.yml | 0 .../haproxy-sni}/gitea-www/index.html | 0 .../haproxy-sni}/gitea.Caddyfile | 0 .../haproxy-certificates/codeberg.org.pem | 0 .../haproxy-certificates/codeberg.org.pem.key | 0 .../haproxy-sni}/haproxy.cfg | 0 .../haproxy-sni}/pages-www/index.html | 0 .../haproxy-sni}/pages.Caddyfile | 0 {haproxy-sni => examples/haproxy-sni}/test.sh | 0 14 files changed, 23 insertions(+), 18 deletions(-) rename {haproxy-sni => examples/haproxy-sni}/.gitignore (100%) rename {haproxy-sni => examples/haproxy-sni}/README.md (100%) rename {haproxy-sni => examples/haproxy-sni}/dhparam.pem (100%) rename {haproxy-sni => examples/haproxy-sni}/docker-compose.yml (100%) rename {haproxy-sni => examples/haproxy-sni}/gitea-www/index.html (100%) rename {haproxy-sni => examples/haproxy-sni}/gitea.Caddyfile (100%) rename {haproxy-sni => examples/haproxy-sni}/haproxy-certificates/codeberg.org.pem (100%) rename {haproxy-sni => examples/haproxy-sni}/haproxy-certificates/codeberg.org.pem.key (100%) rename {haproxy-sni => examples/haproxy-sni}/haproxy.cfg (100%) rename {haproxy-sni => examples/haproxy-sni}/pages-www/index.html (100%) rename {haproxy-sni => examples/haproxy-sni}/pages.Caddyfile (100%) rename {haproxy-sni => examples/haproxy-sni}/test.sh (100%) diff --git a/FEATURES.md b/FEATURES.md index 7560a1d..3d2f394 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,13 +2,19 @@ ## Custom domains -... +Custom domains can be used by creating a `.domains` file with the domain name, e.g.: + +```text +codeberg.page +``` + +You also have to set some DNS records, see the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/using-custom-domain/). ## Redirects Redirects can be created with a `_redirects` file with the following format: -``` +```text # Comment from to [status] ``` @@ -30,7 +36,7 @@ from to [status] Redirects all paths to `/index.html` for single-page apps. -``` +```text /* /index.html 200 ``` @@ -38,7 +44,7 @@ Redirects all paths to `/index.html` for single-page apps. Redirects every path under `/articles` to `/posts` while keeping the path. -``` +```text /articles/* /posts/:splat 302 ``` diff --git a/README.md b/README.md index 9ec3aab..39caa24 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,7 @@ It is suitable to be deployed by other Gitea instances, too, to offer static pag **End user documentation** can mainly be found at the [Wiki](https://codeberg.org/Codeberg/pages-server/wiki/Overview) and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/). - - Get It On Codeberg + Get It On Codeberg ## Quickstart @@ -61,18 +60,18 @@ but if you want to run it on a shared IP address (and not a standalone), you'll need to configure your reverse proxy not to terminate the TLS connections, but forward the requests on the IP level to the Pages Server. -You can check out a proof of concept in the `haproxy-sni` folder, -and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/haproxy-sni/haproxy.cfg#L38). +You can check out a proof of concept in the `examples/haproxy-sni` folder, +and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/examples/haproxy-sni/haproxy.cfg#L38). -### Environment +### Environment Variables - `HOST` & `PORT` (default: `[::]` & `443`): listen address. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. -- `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources. +- `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources (must be subdomain of `PAGES_DOMAIN`). - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. -- `RAW_INFO_PAGE` (default: https://docs.codeberg.org/pages/raw-content/): info page for raw resources, shown if no resource is provided. -- `ACME_API` (default: https://acme-v02.api.letsencrypt.org/directory): set this to https://acme.mock.director to use invalid certificates without any verification (great for debugging). +- `RAW_INFO_PAGE` (default: ): info page for raw resources, shown if no resource is provided. +- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. @@ -80,10 +79,9 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. - See https://go-acme.github.io/lego/dns/ for available values & additional environment variables. + See for available values & additional environment variables. - `LOG_LEVEL` (default: warn): Set this to specify the level of logging. - ## Contributing to the development The Codeberg team is very open to your contribution. @@ -119,7 +117,8 @@ Make sure you have [golang](https://go.dev) v1.20 or newer and [just](https://ju run `just dev` now this pages should work: - - https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg - - https://momar.localhost.mock.directory:4430/ci-testing/ - - https://momar.localhost.mock.directory:4430/pag/@master/ - - https://mock-pages.codeberg-test.org:4430/README.md + +- +- +- +- diff --git a/haproxy-sni/.gitignore b/examples/haproxy-sni/.gitignore similarity index 100% rename from haproxy-sni/.gitignore rename to examples/haproxy-sni/.gitignore diff --git a/haproxy-sni/README.md b/examples/haproxy-sni/README.md similarity index 100% rename from haproxy-sni/README.md rename to examples/haproxy-sni/README.md diff --git a/haproxy-sni/dhparam.pem b/examples/haproxy-sni/dhparam.pem similarity index 100% rename from haproxy-sni/dhparam.pem rename to examples/haproxy-sni/dhparam.pem diff --git a/haproxy-sni/docker-compose.yml b/examples/haproxy-sni/docker-compose.yml similarity index 100% rename from haproxy-sni/docker-compose.yml rename to examples/haproxy-sni/docker-compose.yml diff --git a/haproxy-sni/gitea-www/index.html b/examples/haproxy-sni/gitea-www/index.html similarity index 100% rename from haproxy-sni/gitea-www/index.html rename to examples/haproxy-sni/gitea-www/index.html diff --git a/haproxy-sni/gitea.Caddyfile b/examples/haproxy-sni/gitea.Caddyfile similarity index 100% rename from haproxy-sni/gitea.Caddyfile rename to examples/haproxy-sni/gitea.Caddyfile diff --git a/haproxy-sni/haproxy-certificates/codeberg.org.pem b/examples/haproxy-sni/haproxy-certificates/codeberg.org.pem similarity index 100% rename from haproxy-sni/haproxy-certificates/codeberg.org.pem rename to examples/haproxy-sni/haproxy-certificates/codeberg.org.pem diff --git a/haproxy-sni/haproxy-certificates/codeberg.org.pem.key b/examples/haproxy-sni/haproxy-certificates/codeberg.org.pem.key similarity index 100% rename from haproxy-sni/haproxy-certificates/codeberg.org.pem.key rename to examples/haproxy-sni/haproxy-certificates/codeberg.org.pem.key diff --git a/haproxy-sni/haproxy.cfg b/examples/haproxy-sni/haproxy.cfg similarity index 100% rename from haproxy-sni/haproxy.cfg rename to examples/haproxy-sni/haproxy.cfg diff --git a/haproxy-sni/pages-www/index.html b/examples/haproxy-sni/pages-www/index.html similarity index 100% rename from haproxy-sni/pages-www/index.html rename to examples/haproxy-sni/pages-www/index.html diff --git a/haproxy-sni/pages.Caddyfile b/examples/haproxy-sni/pages.Caddyfile similarity index 100% rename from haproxy-sni/pages.Caddyfile rename to examples/haproxy-sni/pages.Caddyfile diff --git a/haproxy-sni/test.sh b/examples/haproxy-sni/test.sh similarity index 100% rename from haproxy-sni/test.sh rename to examples/haproxy-sni/test.sh From cbb2ce6d0732bcf2372cbc201fc1c1f2733aadba Mon Sep 17 00:00:00 2001 From: crapStone Date: Thu, 16 Nov 2023 17:11:35 +0000 Subject: [PATCH 214/320] add go templating engine for error page and make errors more clear (#260) ping #199 closes #213 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/260 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker.yml | 2 +- README.md | 2 +- go.mod | 15 ++++-- go.sum | 30 +++++++---- html/404.html | 37 ------------- html/error.go | 50 ------------------ html/error.html | 38 -------------- html/error_test.go | 38 -------------- html/html.go | 54 +++++++++++++++++-- html/html_test.go | 54 +++++++++++++++++++ html/templates/error.html | 69 +++++++++++++++++++++++++ server/handler/handler.go | 2 +- server/handler/handler_custom_domain.go | 2 +- server/handler/handler_raw_domain.go | 2 +- server/handler/handler_sub_domain.go | 22 +++++--- server/handler/try.go | 2 +- server/upstream/upstream.go | 16 +++--- 17 files changed, 232 insertions(+), 203 deletions(-) delete mode 100644 html/404.html delete mode 100644 html/error.go delete mode 100644 html/error.html delete mode 100644 html/error_test.go create mode 100644 html/html_test.go create mode 100644 html/templates/error.html diff --git a/.woodpecker.yml b/.woodpecker.yml index 7de8ac8..759e2f1 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,7 +1,7 @@ steps: # use vendor to cache dependencies vendor: - image: golang:1.20 + image: golang:1.21 commands: - go mod vendor diff --git a/README.md b/README.md index 39caa24..fb2a4b9 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Thank you very much. ### Test Server -Make sure you have [golang](https://go.dev) v1.20 or newer and [just](https://just.systems/man/en/) installed. +Make sure you have [golang](https://go.dev) v1.21 or newer and [just](https://just.systems/man/en/) installed. run `just dev` now this pages should work: diff --git a/go.mod b/go.mod index 72eb289..eba292e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module codeberg.org/codeberg/pages -go 1.20 +go 1.21 + +toolchain go1.21.4 require ( code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f @@ -10,6 +12,7 @@ require ( github.com/joho/godotenv v1.4.0 github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 + github.com/microcosm-cc/bluemonday v1.0.26 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.7.0 @@ -35,6 +38,7 @@ require ( github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect github.com/aws/aws-sdk-go v1.39.0 // indirect + github.com/aymerick/douceur v0.2.0 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/cloudflare/cloudflare-go v0.20.0 // indirect @@ -61,6 +65,7 @@ require ( github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/gophercloud/gophercloud v0.16.0 // indirect github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect @@ -115,11 +120,11 @@ require ( github.com/vultr/govultr/v2 v2.7.1 // indirect go.opencensus.io v0.22.3 // indirect go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.3.6 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect google.golang.org/appengine v1.6.5 // indirect diff --git a/go.sum b/go.sum index f78ae18..7ea8b78 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -251,6 +253,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -278,6 +281,8 @@ github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -477,6 +482,8 @@ github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= +github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= @@ -756,8 +763,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -790,7 +797,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -826,8 +834,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -899,11 +908,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -911,8 +921,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -963,7 +974,8 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/html/404.html b/html/404.html deleted file mode 100644 index 7c721b5..0000000 --- a/html/404.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - %status% - - - - - - - - - -

- Page not found! -

-
- Sorry, but this page couldn't be found or is inaccessible (%status%).
- We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs! -
- - - Static pages made easy - Codeberg Pages - - - diff --git a/html/error.go b/html/error.go deleted file mode 100644 index 206b123..0000000 --- a/html/error.go +++ /dev/null @@ -1,50 +0,0 @@ -package html - -import ( - "html/template" - "net/http" - "strconv" - "strings" - - "codeberg.org/codeberg/pages/server/context" -) - -// ReturnErrorPage sets the response status code and writes NotFoundPage to the response body, -// with "%status%" and %message% replaced with the provided statusCode and msg -func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) { - ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8") - ctx.RespWriter.WriteHeader(statusCode) - - msg = generateResponse(msg, statusCode) - - _, _ = ctx.RespWriter.Write([]byte(msg)) -} - -// TODO: use template engine -func generateResponse(msg string, statusCode int) string { - if msg == "" { - msg = strings.ReplaceAll(NotFoundPage, - "%status%", - strconv.Itoa(statusCode)+" "+errorMessage(statusCode)) - } else { - msg = strings.ReplaceAll( - strings.ReplaceAll(ErrorPage, "%message%", template.HTMLEscapeString(msg)), - "%status%", - http.StatusText(statusCode)) - } - - return msg -} - -func errorMessage(statusCode int) string { - message := http.StatusText(statusCode) - - switch statusCode { - case http.StatusMisdirectedRequest: - message += " - domain not specified in .domains file" - case http.StatusFailedDependency: - message += " - target repo/branch doesn't exist or is private" - } - - return message -} diff --git a/html/error.html b/html/error.html deleted file mode 100644 index f1975f7..0000000 --- a/html/error.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - %status% - - - - - - - - - -

- %status%! -

-
- Sorry, but this page couldn't be served.
- We got an "%message%"
- We hope this isn't a problem on our end ;) - Make sure to check the troubleshooting section in the Docs! -
- - - Static pages made easy - Codeberg Pages - - - diff --git a/html/error_test.go b/html/error_test.go deleted file mode 100644 index f5da08c..0000000 --- a/html/error_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package html - -import ( - "net/http" - "strings" - "testing" -) - -func TestValidMessage(t *testing.T) { - testString := "requested blacklisted path" - statusCode := http.StatusForbidden - - expected := strings.ReplaceAll( - strings.ReplaceAll(ErrorPage, "%message%", testString), - "%status%", - http.StatusText(statusCode)) - actual := generateResponse(testString, statusCode) - - if expected != actual { - t.Errorf("generated response did not match: expected: '%s', got: '%s'", expected, actual) - } -} - -func TestMessageWithHtml(t *testing.T) { - testString := `abc +func ReturnErrorPage(ctx *context.Context, msg string, statusCode int) { + ctx.RespWriter.Header().Set("Content-Type", "text/html; charset=utf-8") + ctx.RespWriter.WriteHeader(statusCode) + + templateContext := TemplateContext{ + StatusCode: statusCode, + StatusText: http.StatusText(statusCode), + Message: sanitizer.Sanitize(msg), + } + + err := errorTemplate.Execute(ctx.RespWriter, templateContext) + if err != nil { + log.Err(err).Str("message", msg).Int("status", statusCode).Msg("could not write response") + } +} + +func createBlueMondayPolicy() *bluemonday.Policy { + p := bluemonday.NewPolicy() + + p.AllowElements("code") + + return p +} diff --git a/html/html_test.go b/html/html_test.go new file mode 100644 index 0000000..b395bb2 --- /dev/null +++ b/html/html_test.go @@ -0,0 +1,54 @@ +package html + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSanitizerSimpleString(t *testing.T) { + str := "simple text message without any html elements" + + assert.Equal(t, str, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithCodeTag(t *testing.T) { + str := "simple text message with html tag" + + assert.Equal(t, str, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithCodeTagWithAttribute(t *testing.T) { + str := "simple text message with html tag" + expected := "simple text message with html tag" + + assert.Equal(t, expected, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithATag(t *testing.T) { + str := "simple text message with a link to another page" + expected := "simple text message with a link to another page" + + assert.Equal(t, expected, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithATagAndHref(t *testing.T) { + str := "simple text message with a link to another page" + expected := "simple text message with a link to another page" + + assert.Equal(t, expected, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithImgTag(t *testing.T) { + str := "simple text message with a \"not" + expected := "simple text message with a " + + assert.Equal(t, expected, sanitizer.Sanitize(str)) +} + +func TestSanitizerStringWithImgTagAndOnerrorAttribute(t *testing.T) { + str := "simple text message with a \"not" + expected := "simple text message with a " + + assert.Equal(t, expected, sanitizer.Sanitize(str)) +} diff --git a/html/templates/error.html b/html/templates/error.html new file mode 100644 index 0000000..6094a26 --- /dev/null +++ b/html/templates/error.html @@ -0,0 +1,69 @@ + + + + + + {{.StatusText}} + + + + + + + + + + +

{{.StatusText}} ({{.StatusCode}})!

+
+

Sorry, but this page couldn't be served.

+

"{{.Message}}"

+

+ We hope this isn't a problem on our end ;) - Make sure to check the + troubleshooting section in the Docs! +

+
+ + + Static pages made easy - + Codeberg Pages + + + diff --git a/server/handler/handler.go b/server/handler/handler.go index fbdafd7..a3011f3 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -65,7 +65,7 @@ func Handler(mainDomainSuffix, rawDomain string, // Block blacklisted paths (like ACME challenges) for _, blacklistedPath := range blacklistedPaths { if strings.HasPrefix(ctx.Path(), blacklistedPath) { - html.ReturnErrorPage(ctx, "requested blacklisted path", http.StatusForbidden) + html.ReturnErrorPage(ctx, "requested path is blacklisted", http.StatusForbidden) return } } diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 299d7cf..8742be4 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -49,7 +49,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g }, canonicalLink); works { canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) if !valid { - html.ReturnErrorPage(ctx, "", http.StatusMisdirectedRequest) + html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) return } else if canonicalDomain != trimmedHost { // only redirect if the target is also a codeberg page! diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go index aa41c52..b87991e 100644 --- a/server/handler/handler_raw_domain.go +++ b/server/handler/handler_raw_domain.go @@ -61,7 +61,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage(ctx, - fmt.Sprintf("raw domain could not find repo '%s/%s' or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo), + fmt.Sprintf("raw domain could not find repo %s/%s or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo), http.StatusNotFound) } } diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 8731bec..e7cb3a6 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -55,9 +55,11 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite log.Trace().Msg("tryUpstream: serve with specified repo and branch") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { - html.ReturnErrorPage(ctx, - fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), - http.StatusFailedDependency) + html.ReturnErrorPage( + ctx, + formatSetBranchNotFoundMessage(targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + http.StatusFailedDependency, + ) } return } @@ -85,9 +87,11 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { - html.ReturnErrorPage(ctx, - fmt.Sprintf("explizite set branch %q do not exist at '%s/%s'", targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), - http.StatusFailedDependency) + html.ReturnErrorPage( + ctx, + formatSetBranchNotFoundMessage(targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + http.StatusFailedDependency, + ) } return } @@ -143,6 +147,10 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite // Couldn't find a valid repo/branch html.ReturnErrorPage(ctx, - fmt.Sprintf("could not find a valid repository[%s]", targetRepo), + fmt.Sprintf("could not find a valid repository or branch for repository: %s", targetRepo), http.StatusNotFound) } + +func formatSetBranchNotFoundMessage(branch, owner, repo string) string { + return fmt.Sprintf("explicitly set branch %q does not exist at %s/%s", branch, owner, repo) +} diff --git a/server/handler/try.go b/server/handler/try.go index 6cfe08e..838ae27 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -41,7 +41,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, // Try to request the file from the Gitea API if !options.Upstream(ctx, giteaClient, redirectsCache) { - html.ReturnErrorPage(ctx, "", ctx.StatusCode) + html.ReturnErrorPage(ctx, "gitea client failed", ctx.StatusCode) } } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index f97c6ae..1a444e4 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -53,11 +53,11 @@ type Options struct { } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) (final bool) { +func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) bool { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() if o.TargetOwner == "" || o.TargetRepo == "" { - html.ReturnErrorPage(ctx, "either repo owner or name info is missing", http.StatusBadRequest) + html.ReturnErrorPage(ctx, "gitea client: either repo owner or name info is missing", http.StatusBadRequest) return true } @@ -67,7 +67,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // handle 404 if err != nil && errors.Is(err, gitea.ErrorNotFound) || !branchExist { html.ReturnErrorPage(ctx, - fmt.Sprintf("branch %q for '%s/%s' not found", o.TargetBranch, o.TargetOwner, o.TargetRepo), + fmt.Sprintf("branch %q for %s/%s not found", o.TargetBranch, o.TargetOwner, o.TargetRepo), http.StatusNotFound) return true } @@ -75,7 +75,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // handle unexpected errors if err != nil { html.ReturnErrorPage(ctx, - fmt.Sprintf("could not get timestamp of branch %q: %v", o.TargetBranch, err), + fmt.Sprintf("could not get timestamp of branch %q: '%v'", o.TargetBranch, err), http.StatusFailedDependency) return true } @@ -153,16 +153,16 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi var msg string if err != nil { - msg = "gitea client returned unexpected error" + msg = "gitea client: returned unexpected error" log.Error().Err(err).Msg(msg) - msg = fmt.Sprintf("%s: %v", msg, err) + msg = fmt.Sprintf("%s: '%v'", msg, err) } if reader == nil { - msg = "gitea client returned no reader" + msg = "gitea client: returned no reader" log.Error().Msg(msg) } if statusCode != http.StatusOK { - msg = fmt.Sprintf("Couldn't fetch contents (status code %d)", statusCode) + msg = fmt.Sprintf("gitea client: couldn't fetch contents: %d - %s", statusCode, http.StatusText(statusCode)) log.Error().Msg(msg) } From fffb8ffcb68d44572830bf03f90b4fa5b9db262e Mon Sep 17 00:00:00 2001 From: crapStone Date: Thu, 16 Nov 2023 17:33:39 +0000 Subject: [PATCH 215/320] remove use of rawInfoPage redirect (#261) closes #244 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/261 Co-authored-by: crapStone Co-committed-by: crapStone --- cmd/flags.go | 7 ------- cmd/main.go | 2 -- server/handler/handler.go | 3 +-- server/handler/handler_raw_domain.go | 10 +++++++--- server/handler/handler_test.go | 1 - 5 files changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/flags.go b/cmd/flags.go index a71dd35..7ac94e6 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -72,13 +72,6 @@ var ( EnvVars: []string{"RAW_DOMAIN"}, Value: "raw.codeberg.page", }, - // RawInfoPage will be shown (with a redirect) when trying to access RawDomain directly (or without owner/repo/path). - &cli.StringFlag{ - Name: "raw-info-page", - Usage: "will be shown (with a redirect) when trying to access $RAW_DOMAIN directly (or without owner/repo/path)", - EnvVars: []string{"RAW_INFO_PAGE"}, - Value: "https://docs.codeberg.org/codeberg-pages/raw-content/", - }, // ######################### // ### Page Server Setup ### diff --git a/cmd/main.go b/cmd/main.go index 84915c9..683e859 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -47,7 +47,6 @@ func Serve(ctx *cli.Context) error { rawDomain := ctx.String("raw-domain") defaultBranches := ctx.StringSlice("pages-branch") mainDomainSuffix := ctx.String("pages-domain") - rawInfoPage := ctx.String("raw-info-page") listeningHost := ctx.String("host") listeningSSLPort := ctx.Uint("port") listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort) @@ -137,7 +136,6 @@ func Serve(ctx *cli.Context) error { // Create ssl handler based on settings sslHandler := handler.Handler(mainDomainSuffix, rawDomain, giteaClient, - rawInfoPage, BlacklistedPaths, allowedCorsDomains, defaultBranches, dnsLookupCache, canonicalDomainCache, redirectsCache) diff --git a/server/handler/handler.go b/server/handler/handler.go index a3011f3..7da5d39 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -21,7 +21,6 @@ const ( // Handler handles a single HTTP request to the web server. func Handler(mainDomainSuffix, rawDomain string, giteaClient *gitea.Client, - rawInfoPage string, blacklistedPaths, allowedCorsDomains []string, defaultPagesBranches []string, dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey, @@ -89,7 +88,7 @@ func Handler(mainDomainSuffix, rawDomain string, if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { log.Debug().Msg("raw domain request detected") handleRaw(log, ctx, giteaClient, - mainDomainSuffix, rawInfoPage, + mainDomainSuffix, trimmedHost, pathElements, canonicalDomainCache, redirectsCache) diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go index b87991e..caa8209 100644 --- a/server/handler/handler_raw_domain.go +++ b/server/handler/handler_raw_domain.go @@ -16,7 +16,7 @@ import ( ) func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, - mainDomainSuffix, rawInfoPage string, + mainDomainSuffix string, trimmedHost string, pathElements []string, canonicalDomainCache, redirectsCache cache.SetGetKey, @@ -25,8 +25,12 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie log.Debug().Msg("raw domain") if len(pathElements) < 2 { - // https://{RawDomain}/{owner}/{repo}[/@{branch}]/{path} is required - ctx.Redirect(rawInfoPage, http.StatusTemporaryRedirect) + html.ReturnErrorPage( + ctx, + "a url in the form of https://{domain}/{owner}/{repo}[/@{branch}]/{path} is required", + http.StatusBadRequest, + ) + return } diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index 6521633..d04ebda 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -16,7 +16,6 @@ func TestHandlerPerformance(t *testing.T) { testHandler := Handler( "codeberg.page", "raw.codeberg.org", giteaClient, - "https://docs.codeberg.org/pages/raw-content/", []string{"/.well-known/acme-challenge/"}, []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, []string{"pages"}, From dd5124912ecde8246bdcaad223ecde7737928429 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 17 Nov 2023 21:46:52 +0000 Subject: [PATCH 216/320] CI: run on pull only once (#264) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/264 Co-authored-by: 6543 <6543@obermui.de> Co-committed-by: 6543 <6543@obermui.de> --- .woodpecker.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.woodpecker.yml b/.woodpecker.yml index 759e2f1..de8341c 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,3 +1,6 @@ +when: + branch: main + steps: # use vendor to cache dependencies vendor: From 15916444e18c71c066ffe8a7cd37cf272ec115eb Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 18 Jan 2024 14:31:46 +0000 Subject: [PATCH 217/320] Fix panic in formatting not found message (#276) Fix panic that was found in the logs, `targetOpt` is `nil`: http2: panic serving 10.0.3.1:[...]: runtime error: invalid memory address or nil pointer dereference net/http.(*http2serverConn).runHandler.func1() /usr/local/go/src/net/http/h2_bundle.go:6104 +0x145 panic({0x19c6820, 0x2d66db0}) /usr/local/go/src/runtime/panic.go:884 +0x213 codeberg.org/codeberg/pages/server/handler.handleSubDomain({{0x2008c68, 0xc00047df90}, 0x2, {0x0, 0x0}, {0xc0fe3ef800, 0x55, 0x1f4}, {0xc00047dfa0, 0x1, ...}, ...}, ...) /woodpecker/src/codeberg.org/Codeberg/pages-server/server/handler/handler_sub_domain.go:59 +0x5e0 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/276 Reviewed-by: crapStone Co-authored-by: Gusted Co-committed-by: Gusted --- server/handler/handler_sub_domain.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index e7cb3a6..6c14393 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -57,7 +57,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite } else { html.ReturnErrorPage( ctx, - formatSetBranchNotFoundMessage(targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + formatSetBranchNotFoundMessage(pathElements[1][1:], targetOwner, pathElements[0]), http.StatusFailedDependency, ) } @@ -89,7 +89,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite } else { html.ReturnErrorPage( ctx, - formatSetBranchNotFoundMessage(targetOpt.TargetBranch, targetOpt.TargetOwner, targetOpt.TargetRepo), + formatSetBranchNotFoundMessage(targetBranch, targetOwner, defaultPagesRepo), http.StatusFailedDependency, ) } From a09bee68adf43c2ade9eb3264708f29b68d599ea Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Thu, 18 Jan 2024 20:35:32 +0000 Subject: [PATCH 218/320] Meta: Redirect user support to Codeberg/Community (#277) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/277 Reviewed-by: crapStone Co-authored-by: Panagiotis "Ivory" Vasilopoulos Co-committed-by: Panagiotis "Ivory" Vasilopoulos --- .gitea/ISSUE_TEMPLATE/config.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitea/ISSUE_TEMPLATE/config.yml diff --git a/.gitea/ISSUE_TEMPLATE/config.yml b/.gitea/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..5a9cce6 --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Codeberg Pages Usage Support + url: https://codeberg.org/Codeberg/Community/issues/ + about: If you need help with configuring Codeberg Pages on codeberg.org, please go here. From c1fbe861fe793eb335cab63a524e5ae2bb3fb7f1 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 11 Feb 2024 12:43:25 +0000 Subject: [PATCH 219/320] rename gitea to forge in html error messages (#287) closes #286 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/287 Reviewed-by: Andreas Shimokawa Co-authored-by: crapStone Co-committed-by: crapStone --- server/certificates/certificates.go | 2 +- server/handler/try.go | 2 +- server/upstream/domains.go | 2 +- server/upstream/upstream.go | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 3ae891a..4fb99fa 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -199,7 +199,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") - if useDnsProvider && len(domains[0]) > 0 && domains[0][0] == '*' { + if useDnsProvider && domains[0] != "" && domains[0][0] == '*' { domains = domains[1:] } diff --git a/server/handler/try.go b/server/handler/try.go index 838ae27..bb79dce 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -41,7 +41,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, // Try to request the file from the Gitea API if !options.Upstream(ctx, giteaClient, redirectsCache) { - html.ReturnErrorPage(ctx, "gitea client failed", ctx.StatusCode) + html.ReturnErrorPage(ctx, "forge client failed", ctx.StatusCode) } } diff --git a/server/upstream/domains.go b/server/upstream/domains.go index eb30394..230f268 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -41,7 +41,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, domain = strings.TrimSpace(domain) domain = strings.TrimPrefix(domain, "http://") domain = strings.TrimPrefix(domain, "https://") - if len(domain) > 0 && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { + if domain != "" && !strings.HasPrefix(domain, "#") && !strings.ContainsAny(domain, "\t /") && strings.ContainsRune(domain, '.') { domains = append(domains, domain) } if domain == actualDomain { diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 1a444e4..4a85503 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -57,7 +57,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() if o.TargetOwner == "" || o.TargetRepo == "" { - html.ReturnErrorPage(ctx, "gitea client: either repo owner or name info is missing", http.StatusBadRequest) + html.ReturnErrorPage(ctx, "forge client: either repo owner or name info is missing", http.StatusBadRequest) return true } @@ -153,16 +153,16 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi var msg string if err != nil { - msg = "gitea client: returned unexpected error" + msg = "forge client: returned unexpected error" log.Error().Err(err).Msg(msg) msg = fmt.Sprintf("%s: '%v'", msg, err) } if reader == nil { - msg = "gitea client: returned no reader" + msg = "forge client: returned no reader" log.Error().Msg(msg) } if statusCode != http.StatusOK { - msg = fmt.Sprintf("gitea client: couldn't fetch contents: %d - %s", statusCode, http.StatusText(statusCode)) + msg = fmt.Sprintf("forge client: couldn't fetch contents: %d - %s", statusCode, http.StatusText(statusCode)) log.Error().Msg(msg) } From 7e80ade24b8aac072804122b343a2a1a70667983 Mon Sep 17 00:00:00 2001 From: crapStone Date: Thu, 15 Feb 2024 16:08:29 +0000 Subject: [PATCH 220/320] Add config file and rework cli parsing and passing of config values (#263) Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/263 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: crapStone Co-committed-by: crapStone --- .env-dev | 11 + .vscode/launch.json | 26 + Justfile | 15 +- {cmd => cli}/certs.go | 6 +- {cmd => cli}/flags.go | 52 +- cli/setup.go | 39 ++ cmd/main.go | 150 ------ cmd/setup.go | 64 --- config/assets/test_config.toml | 33 ++ config/config.go | 46 ++ config/setup.go | 147 ++++++ config/setup_test.go | 596 ++++++++++++++++++++++ example_config.toml | 32 ++ go.mod | 8 +- go.sum | 16 +- integration/main_test.go | 17 +- main.go | 20 +- server/acme/client.go | 26 + server/cache/interface.go | 3 +- server/cache/{setup.go => memory.go} | 2 +- server/certificates/acme_client.go | 11 +- server/certificates/acme_config.go | 34 +- server/certificates/cached_challengers.go | 6 +- server/certificates/certificates.go | 2 +- server/dns/dns.go | 2 +- server/gitea/cache.go | 4 +- server/gitea/client.go | 28 +- server/handler/handler.go | 30 +- server/handler/handler_custom_domain.go | 2 +- server/handler/handler_raw_domain.go | 2 +- server/handler/handler_sub_domain.go | 2 +- server/handler/handler_test.go | 29 +- server/handler/try.go | 4 +- server/startup.go | 141 +++++ server/upstream/domains.go | 2 +- server/upstream/redirects.go | 4 +- server/upstream/upstream.go | 2 +- 37 files changed, 1270 insertions(+), 344 deletions(-) create mode 100644 .env-dev create mode 100644 .vscode/launch.json rename {cmd => cli}/certs.go (92%) rename {cmd => cli}/flags.go (80%) create mode 100644 cli/setup.go delete mode 100644 cmd/main.go delete mode 100644 cmd/setup.go create mode 100644 config/assets/test_config.toml create mode 100644 config/config.go create mode 100644 config/setup.go create mode 100644 config/setup_test.go create mode 100644 example_config.toml create mode 100644 server/acme/client.go rename server/cache/{setup.go => memory.go} (69%) create mode 100644 server/startup.go diff --git a/.env-dev b/.env-dev new file mode 100644 index 0000000..2286005 --- /dev/null +++ b/.env-dev @@ -0,0 +1,11 @@ +ACME_API=https://acme.mock.directory +ACME_ACCEPT_TERMS=true +PAGES_DOMAIN=localhost.mock.directory +RAW_DOMAIN=raw.localhost.mock.directory +PAGES_BRANCHES=pages,master,main +GITEA_ROOT=https://codeberg.org +PORT=4430 +HTTP_PORT=8880 +ENABLE_HTTP_SERVER=true +LOG_LEVEL=trace +ACME_ACCOUNT_CONFIG=integration/acme-account.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d2cc8d1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch PagesServer", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/main.go", + "args": ["sqlite", "sqlite_unlock_notify", "netgo"], + "envFile": "${workspaceFolder}/.env-dev" + }, + { + "name": "Launch PagesServer integration test", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/integration/main_test.go", + "args": ["codeberg.org/codeberg/pages/integration/..."], + "buildFlags": ["-tags", "'integration sqlite sqlite_unlock_notify netgo'"] + } + ] +} diff --git a/Justfile b/Justfile index 0b8f814..0bf38a3 100644 --- a/Justfile +++ b/Justfile @@ -4,14 +4,9 @@ TAGS := 'sqlite sqlite_unlock_notify netgo' dev: #!/usr/bin/env bash set -euxo pipefail - export ACME_API=https://acme.mock.directory - export ACME_ACCEPT_TERMS=true - export PAGES_DOMAIN=localhost.mock.directory - export RAW_DOMAIN=raw.localhost.mock.directory - export PORT=4430 - export HTTP_PORT=8880 - export ENABLE_HTTP_SERVER=true - export LOG_LEVEL=trace + set -a # automatically export all variables + source .env-dev + set +a go run -tags '{{TAGS}}' . build: @@ -42,10 +37,10 @@ tool-gofumpt: fi test: - go test -race -cover -tags '{{TAGS}}' codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ + go test -race -cover -tags '{{TAGS}}' codeberg.org/codeberg/pages/config/ codeberg.org/codeberg/pages/html/ codeberg.org/codeberg/pages/server/... test-run TEST: - go test -race -tags '{{TAGS}}' -run "^{{TEST}}$" codeberg.org/codeberg/pages/server/... codeberg.org/codeberg/pages/html/ + go test -race -tags '{{TAGS}}' -run "^{{TEST}}$" codeberg.org/codeberg/pages/config/ codeberg.org/codeberg/pages/html/ codeberg.org/codeberg/pages/server/... integration: go test -race -tags 'integration {{TAGS}}' codeberg.org/codeberg/pages/integration/... diff --git a/cmd/certs.go b/cli/certs.go similarity index 92% rename from cmd/certs.go rename to cli/certs.go index 6012b6e..76df7f0 100644 --- a/cmd/certs.go +++ b/cli/certs.go @@ -1,4 +1,4 @@ -package cmd +package cli import ( "fmt" @@ -26,7 +26,7 @@ var Certs = &cli.Command{ } func listCerts(ctx *cli.Context) error { - certDB, closeFn, err := openCertDB(ctx) + certDB, closeFn, err := OpenCertDB(ctx) if err != nil { return err } @@ -53,7 +53,7 @@ func removeCert(ctx *cli.Context) error { domains := ctx.Args().Slice() - certDB, closeFn, err := openCertDB(ctx) + certDB, closeFn, err := OpenCertDB(ctx) if err != nil { return err } diff --git a/cmd/flags.go b/cli/flags.go similarity index 80% rename from cmd/flags.go rename to cli/flags.go index 7ac94e6..097cf4f 100644 --- a/cmd/flags.go +++ b/cli/flags.go @@ -1,4 +1,4 @@ -package cmd +package cli import ( "github.com/urfave/cli/v2" @@ -29,26 +29,35 @@ var ( Name: "gitea-root", Usage: "specifies the root URL of the Gitea instance, without a trailing slash.", EnvVars: []string{"GITEA_ROOT"}, - Value: "https://codeberg.org", }, // GiteaApiToken specifies an api token for the Gitea instance &cli.StringFlag{ Name: "gitea-api-token", Usage: "specifies an api token for the Gitea instance", EnvVars: []string{"GITEA_API_TOKEN"}, - Value: "", }, &cli.BoolFlag{ Name: "enable-lfs-support", Usage: "enable lfs support, require gitea >= v1.17.0 as backend", EnvVars: []string{"ENABLE_LFS_SUPPORT"}, - Value: true, + Value: false, }, &cli.BoolFlag{ Name: "enable-symlink-support", Usage: "follow symlinks if enabled, require gitea >= v1.18.0 as backend", EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, - Value: true, + Value: false, + }, + &cli.StringFlag{ + Name: "default-mime-type", + Usage: "specifies the default mime type for files that don't have a specific mime type.", + EnvVars: []string{"DEFAULT_MIME_TYPE"}, + Value: "application/octet-stream", + }, + &cli.StringSliceFlag{ + Name: "forbidden-mime-types", + Usage: "specifies the forbidden mime types. Use this flag multiple times for multiple mime types.", + EnvVars: []string{"FORBIDDEN_MIME_TYPES"}, }, // ########################### @@ -61,7 +70,6 @@ var ( Name: "pages-domain", Usage: "specifies the main domain (starting with a dot) for which subdomains shall be served as static pages", EnvVars: []string{"PAGES_DOMAIN"}, - Value: "codeberg.page", }, // RawDomain specifies the domain from which raw repository content shall be served in the following format: // https://{RawDomain}/{owner}/{repo}[/{branch|tag|commit}/{version}]/{filepath...} @@ -70,7 +78,6 @@ var ( Name: "raw-domain", Usage: "specifies the domain from which raw repository content shall be served, not set disable raw content hosting", EnvVars: []string{"RAW_DOMAIN"}, - Value: "raw.codeberg.page", }, // ######################### @@ -98,19 +105,38 @@ var ( Name: "enable-http-server", Usage: "start a http server to redirect to https and respond to http acme challenges", EnvVars: []string{"ENABLE_HTTP_SERVER"}, + Value: false, }, + // Default branches to fetch assets from + &cli.StringSliceFlag{ + Name: "pages-branch", + Usage: "define a branch to fetch assets from. Use this flag multiple times for multiple branches.", + EnvVars: []string{"PAGES_BRANCHES"}, + Value: cli.NewStringSlice("pages"), + }, + + &cli.StringSliceFlag{ + Name: "allowed-cors-domains", + Usage: "specify allowed CORS domains. Use this flag multiple times for multiple domains.", + EnvVars: []string{"ALLOWED_CORS_DOMAINS"}, + }, + &cli.StringSliceFlag{ + Name: "blacklisted-paths", + Usage: "return an error on these url paths.Use this flag multiple times for multiple paths.", + EnvVars: []string{"BLACKLISTED_PATHS"}, + }, + &cli.StringFlag{ Name: "log-level", Value: "warn", Usage: "specify at which log level should be logged. Possible options: info, warn, error, fatal", EnvVars: []string{"LOG_LEVEL"}, }, - // Default branches to fetch assets from - &cli.StringSliceFlag{ - Name: "pages-branch", - Usage: "define a branch to fetch assets from", - EnvVars: []string{"PAGES_BRANCHES"}, - Value: cli.NewStringSlice("pages"), + &cli.StringFlag{ + Name: "config-file", + Usage: "specify the location of the config file", + Aliases: []string{"config"}, + EnvVars: []string{"CONFIG_FILE"}, }, // ############################ diff --git a/cli/setup.go b/cli/setup.go new file mode 100644 index 0000000..6bbff40 --- /dev/null +++ b/cli/setup.go @@ -0,0 +1,39 @@ +package cli + +import ( + "fmt" + + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" + + "codeberg.org/codeberg/pages/server/database" + "codeberg.org/codeberg/pages/server/version" +) + +func CreatePagesApp() *cli.App { + app := cli.NewApp() + app.Name = "pages-server" + app.Version = version.Version + app.Usage = "pages server" + app.Flags = ServerFlags + app.Commands = []*cli.Command{ + Certs, + } + + return app +} + +func OpenCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) { + certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) + if err != nil { + return nil, nil, fmt.Errorf("could not connect to database: %w", err) + } + + closeFn = func() { + if err := certDB.Close(); err != nil { + log.Error().Err(err) + } + } + + return certDB, closeFn, nil +} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 683e859..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,150 +0,0 @@ -package cmd - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "net/http" - "os" - "strings" - "time" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/urfave/cli/v2" - - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/certificates" - "codeberg.org/codeberg/pages/server/gitea" - "codeberg.org/codeberg/pages/server/handler" -) - -// AllowedCorsDomains lists the domains for which Cross-Origin Resource Sharing is allowed. -// TODO: make it a flag -var AllowedCorsDomains = []string{ - "fonts.codeberg.org", - "design.codeberg.org", -} - -// BlacklistedPaths specifies forbidden path prefixes for all Codeberg Pages. -// TODO: Make it a flag too -var BlacklistedPaths = []string{ - "/.well-known/acme-challenge/", -} - -// Serve sets up and starts the web server. -func Serve(ctx *cli.Context) error { - // Initialize the logger. - logLevel, err := zerolog.ParseLevel(ctx.String("log-level")) - if err != nil { - return err - } - log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) - - giteaRoot := ctx.String("gitea-root") - giteaAPIToken := ctx.String("gitea-api-token") - rawDomain := ctx.String("raw-domain") - defaultBranches := ctx.StringSlice("pages-branch") - mainDomainSuffix := ctx.String("pages-domain") - listeningHost := ctx.String("host") - listeningSSLPort := ctx.Uint("port") - listeningSSLAddress := fmt.Sprintf("%s:%d", listeningHost, listeningSSLPort) - listeningHTTPAddress := fmt.Sprintf("%s:%d", listeningHost, ctx.Uint("http-port")) - enableHTTPServer := ctx.Bool("enable-http-server") - - allowedCorsDomains := AllowedCorsDomains - if rawDomain != "" { - allowedCorsDomains = append(allowedCorsDomains, rawDomain) - } - - // Make sure MainDomain has a trailing dot - if !strings.HasPrefix(mainDomainSuffix, ".") { - mainDomainSuffix = "." + mainDomainSuffix - } - - if len(defaultBranches) == 0 { - return fmt.Errorf("no default branches set (PAGES_BRANCHES)") - } - - // Init ssl cert database - certDB, closeFn, err := openCertDB(ctx) - if err != nil { - return err - } - defer closeFn() - - keyCache := cache.NewKeyValueCache() - challengeCache := cache.NewKeyValueCache() - // canonicalDomainCache stores canonical domains - canonicalDomainCache := cache.NewKeyValueCache() - // dnsLookupCache stores DNS lookups for custom domains - dnsLookupCache := cache.NewKeyValueCache() - // redirectsCache stores redirects in _redirects files - redirectsCache := cache.NewKeyValueCache() - // clientResponseCache stores responses from the Gitea server - clientResponseCache := cache.NewKeyValueCache() - - giteaClient, err := gitea.NewClient(giteaRoot, giteaAPIToken, clientResponseCache, ctx.Bool("enable-symlink-support"), ctx.Bool("enable-lfs-support")) - if err != nil { - return fmt.Errorf("could not create new gitea client: %v", err) - } - - acmeClient, err := createAcmeClient(ctx, enableHTTPServer, challengeCache) - if err != nil { - return err - } - - if err := certificates.SetupMainDomainCertificates(mainDomainSuffix, acmeClient, certDB); err != nil { - return err - } - - // Create listener for SSL connections - log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress) - listener, err := net.Listen("tcp", listeningSSLAddress) - if err != nil { - return fmt.Errorf("couldn't create listener: %v", err) - } - - // Setup listener for SSL connections - listener = tls.NewListener(listener, certificates.TLSConfig(mainDomainSuffix, - giteaClient, - acmeClient, - defaultBranches[0], - keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, - certDB)) - - interval := 12 * time.Hour - certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) - defer cancelCertMaintain() - go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, mainDomainSuffix, certDB) - - if enableHTTPServer { - // Create handler for http->https redirect and http acme challenges - httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, listeningSSLPort) - - // Create listener for http and start listening - go func() { - log.Info().Msgf("Start HTTP server listening on %s", listeningHTTPAddress) - err := http.ListenAndServe(listeningHTTPAddress, httpHandler) - if err != nil { - log.Panic().Err(err).Msg("Couldn't start HTTP fastServer") - } - }() - } - - // Create ssl handler based on settings - sslHandler := handler.Handler(mainDomainSuffix, rawDomain, - giteaClient, - BlacklistedPaths, allowedCorsDomains, - defaultBranches, - dnsLookupCache, canonicalDomainCache, redirectsCache) - - // Start the ssl listener - log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) - if err := http.Serve(listener, sslHandler); err != nil { - log.Panic().Err(err).Msg("Couldn't start fastServer") - } - - return nil -} diff --git a/cmd/setup.go b/cmd/setup.go deleted file mode 100644 index cde4bc9..0000000 --- a/cmd/setup.go +++ /dev/null @@ -1,64 +0,0 @@ -package cmd - -import ( - "errors" - "fmt" - - "github.com/rs/zerolog/log" - "github.com/urfave/cli/v2" - - "codeberg.org/codeberg/pages/server/cache" - "codeberg.org/codeberg/pages/server/certificates" - "codeberg.org/codeberg/pages/server/database" -) - -var ErrAcmeMissConfig = errors.New("ACME client has wrong config") - -func openCertDB(ctx *cli.Context) (certDB database.CertDB, closeFn func(), err error) { - certDB, err = database.NewXormDB(ctx.String("db-type"), ctx.String("db-conn")) - if err != nil { - return nil, nil, fmt.Errorf("could not connect to database: %w", err) - } - - closeFn = func() { - if err := certDB.Close(); err != nil { - log.Error().Err(err) - } - } - - return certDB, closeFn, nil -} - -func createAcmeClient(ctx *cli.Context, enableHTTPServer bool, challengeCache cache.SetGetKey) (*certificates.AcmeClient, error) { - acmeAPI := ctx.String("acme-api-endpoint") - acmeMail := ctx.String("acme-email") - acmeEabHmac := ctx.String("acme-eab-hmac") - acmeEabKID := ctx.String("acme-eab-kid") - acmeAcceptTerms := ctx.Bool("acme-accept-terms") - dnsProvider := ctx.String("dns-provider") - acmeUseRateLimits := ctx.Bool("acme-use-rate-limits") - acmeAccountConf := ctx.String("acme-account-config") - - // check config - if (!acmeAcceptTerms || dnsProvider == "") && acmeAPI != "https://acme.mock.directory" { - return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) - } - if acmeEabHmac != "" && acmeEabKID == "" { - return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) - } else if acmeEabHmac == "" && acmeEabKID != "" { - return nil, fmt.Errorf("%w: ACME_EAB_KID also needs ACME_EAB_HMAC to be set", ErrAcmeMissConfig) - } - - return certificates.NewAcmeClient( - acmeAccountConf, - acmeAPI, - acmeMail, - acmeEabHmac, - acmeEabKID, - dnsProvider, - acmeAcceptTerms, - enableHTTPServer, - acmeUseRateLimits, - challengeCache, - ) -} diff --git a/config/assets/test_config.toml b/config/assets/test_config.toml new file mode 100644 index 0000000..6a2f0d0 --- /dev/null +++ b/config/assets/test_config.toml @@ -0,0 +1,33 @@ +logLevel = 'trace' + +[server] +host = '127.0.0.1' +port = 443 +httpPort = 80 +httpServerEnabled = true +mainDomain = 'codeberg.page' +rawDomain = 'raw.codeberg.page' +allowedCorsDomains = ['fonts.codeberg.org', 'design.codeberg.org'] +blacklistedPaths = ['do/not/use'] + +[gitea] +root = 'codeberg.org' +token = 'XXXXXXXX' +lfsEnabled = true +followSymlinks = true +defaultMimeType = "application/wasm" +forbiddenMimeTypes = ["text/html"] + +[database] +type = 'sqlite' +conn = 'certs.sqlite' + +[ACME] +email = 'a@b.c' +apiEndpoint = 'https://example.com' +acceptTerms = false +useRateLimits = true +eab_hmac = 'asdf' +eab_kid = 'qwer' +dnsProvider = 'cloudflare.com' +accountConfigFile = 'nope' diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..6cb972b --- /dev/null +++ b/config/config.go @@ -0,0 +1,46 @@ +package config + +type Config struct { + LogLevel string `default:"warn"` + Server ServerConfig + Gitea GiteaConfig + Database DatabaseConfig + ACME ACMEConfig +} + +type ServerConfig struct { + Host string `default:"[::]"` + Port uint16 `default:"443"` + HttpPort uint16 `default:"80"` + HttpServerEnabled bool `default:"true"` + MainDomain string + RawDomain string + PagesBranches []string + AllowedCorsDomains []string + BlacklistedPaths []string +} + +type GiteaConfig struct { + Root string + Token string + LFSEnabled bool `default:"false"` + FollowSymlinks bool `default:"false"` + DefaultMimeType string `default:"application/octet-stream"` + ForbiddenMimeTypes []string +} + +type DatabaseConfig struct { + Type string `default:"sqlite3"` + Conn string `default:"certs.sqlite"` +} + +type ACMEConfig struct { + Email string + APIEndpoint string `default:"https://acme-v02.api.letsencrypt.org/directory"` + AcceptTerms bool `default:"false"` + UseRateLimits bool `default:"true"` + EAB_HMAC string + EAB_KID string + DNSProvider string + AccountConfigFile string `default:"acme-account.json"` +} diff --git a/config/setup.go b/config/setup.go new file mode 100644 index 0000000..e774084 --- /dev/null +++ b/config/setup.go @@ -0,0 +1,147 @@ +package config + +import ( + "os" + "path" + + "github.com/creasty/defaults" + "github.com/pelletier/go-toml/v2" + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" +) + +var ALWAYS_BLACKLISTED_PATHS = []string{ + "/.well-known/acme-challenge/", +} + +func NewDefaultConfig() Config { + config := Config{} + if err := defaults.Set(&config); err != nil { + panic(err) + } + + // defaults does not support setting arrays from strings + config.Server.PagesBranches = []string{"main", "master", "pages"} + + return config +} + +func ReadConfig(ctx *cli.Context) (*Config, error) { + config := NewDefaultConfig() + // if config is not given as argument return empty config + if !ctx.IsSet("config-file") { + return &config, nil + } + + configFile := path.Clean(ctx.String("config-file")) + + log.Debug().Str("config-file", configFile).Msg("reading config file") + content, err := os.ReadFile(configFile) + if err != nil { + return nil, err + } + + err = toml.Unmarshal(content, &config) + return &config, err +} + +func MergeConfig(ctx *cli.Context, config *Config) { + if ctx.IsSet("log-level") { + config.LogLevel = ctx.String("log-level") + } + + mergeServerConfig(ctx, &config.Server) + mergeGiteaConfig(ctx, &config.Gitea) + mergeDatabaseConfig(ctx, &config.Database) + mergeACMEConfig(ctx, &config.ACME) +} + +func mergeServerConfig(ctx *cli.Context, config *ServerConfig) { + if ctx.IsSet("host") { + config.Host = ctx.String("host") + } + if ctx.IsSet("port") { + config.Port = uint16(ctx.Uint("port")) + } + if ctx.IsSet("http-port") { + config.HttpPort = uint16(ctx.Uint("http-port")) + } + if ctx.IsSet("enable-http-server") { + config.HttpServerEnabled = ctx.Bool("enable-http-server") + } + if ctx.IsSet("pages-domain") { + config.MainDomain = ctx.String("pages-domain") + } + if ctx.IsSet("raw-domain") { + config.RawDomain = ctx.String("raw-domain") + } + if ctx.IsSet("pages-branch") { + config.PagesBranches = ctx.StringSlice("pages-branch") + } + if ctx.IsSet("allowed-cors-domains") { + config.AllowedCorsDomains = ctx.StringSlice("allowed-cors-domains") + } + if ctx.IsSet("blacklisted-paths") { + config.BlacklistedPaths = ctx.StringSlice("blacklisted-paths") + } + + // add the paths that should always be blacklisted + config.BlacklistedPaths = append(config.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...) +} + +func mergeGiteaConfig(ctx *cli.Context, config *GiteaConfig) { + if ctx.IsSet("gitea-root") { + config.Root = ctx.String("gitea-root") + } + if ctx.IsSet("gitea-api-token") { + config.Token = ctx.String("gitea-api-token") + } + if ctx.IsSet("enable-lfs-support") { + config.LFSEnabled = ctx.Bool("enable-lfs-support") + } + if ctx.IsSet("enable-symlink-support") { + config.FollowSymlinks = ctx.Bool("enable-symlink-support") + } + if ctx.IsSet("default-mime-type") { + config.DefaultMimeType = ctx.String("default-mime-type") + } + if ctx.IsSet("forbidden-mime-types") { + config.ForbiddenMimeTypes = ctx.StringSlice("forbidden-mime-types") + } +} + +func mergeDatabaseConfig(ctx *cli.Context, config *DatabaseConfig) { + if ctx.IsSet("db-type") { + config.Type = ctx.String("db-type") + } + if ctx.IsSet("db-conn") { + config.Conn = ctx.String("db-conn") + } +} + +func mergeACMEConfig(ctx *cli.Context, config *ACMEConfig) { + if ctx.IsSet("acme-email") { + config.Email = ctx.String("acme-email") + } + if ctx.IsSet("acme-api-endpoint") { + config.APIEndpoint = ctx.String("acme-api-endpoint") + } + if ctx.IsSet("acme-accept-terms") { + config.AcceptTerms = ctx.Bool("acme-accept-terms") + } + if ctx.IsSet("acme-use-rate-limits") { + config.UseRateLimits = ctx.Bool("acme-use-rate-limits") + } + if ctx.IsSet("acme-eab-hmac") { + config.EAB_HMAC = ctx.String("acme-eab-hmac") + } + if ctx.IsSet("acme-eab-kid") { + config.EAB_KID = ctx.String("acme-eab-kid") + } + if ctx.IsSet("dns-provider") { + config.DNSProvider = ctx.String("dns-provider") + } + if ctx.IsSet("acme-account-config") { + config.AccountConfigFile = ctx.String("acme-account-config") + } +} diff --git a/config/setup_test.go b/config/setup_test.go new file mode 100644 index 0000000..a863e2f --- /dev/null +++ b/config/setup_test.go @@ -0,0 +1,596 @@ +package config + +import ( + "context" + "os" + "testing" + + "github.com/pelletier/go-toml/v2" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v2" + + cmd "codeberg.org/codeberg/pages/cli" +) + +func runApp(t *testing.T, fn func(*cli.Context) error, args []string) { + app := cmd.CreatePagesApp() + app.Action = fn + + appCtx, appCancel := context.WithCancel(context.Background()) + defer appCancel() + + // os.Args always contains the binary name + args = append([]string{"testing"}, args...) + + err := app.RunContext(appCtx, args) + assert.NoError(t, err) +} + +// fixArrayFromCtx fixes the number of "changed" strings in a string slice according to the number of values in the context. +// This is a workaround because the cli library has a bug where the number of values in the context gets bigger the more tests are run. +func fixArrayFromCtx(ctx *cli.Context, key string, expected []string) []string { + if ctx.IsSet(key) { + ctxSlice := ctx.StringSlice(key) + + if len(ctxSlice) > 1 { + for i := 1; i < len(ctxSlice); i++ { + expected = append([]string{"changed"}, expected...) + } + } + } + + return expected +} + +func readTestConfig() (*Config, error) { + content, err := os.ReadFile("assets/test_config.toml") + if err != nil { + return nil, err + } + + expectedConfig := NewDefaultConfig() + err = toml.Unmarshal(content, &expectedConfig) + if err != nil { + return nil, err + } + + return &expectedConfig, nil +} + +func TestReadConfigShouldReturnEmptyConfigWhenConfigArgEmpty(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg, err := ReadConfig(ctx) + expected := NewDefaultConfig() + assert.Equal(t, &expected, cfg) + + return err + }, + []string{}, + ) +} + +func TestReadConfigShouldReturnConfigFromFileWhenConfigArgPresent(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg, err := ReadConfig(ctx) + if err != nil { + return err + } + + expectedConfig, err := readTestConfig() + if err != nil { + return err + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{"--config-file", "assets/test_config.toml"}, + ) +} + +func TestValuesReadFromConfigFileShouldBeOverwrittenByArgs(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg, err := ReadConfig(ctx) + if err != nil { + return err + } + + MergeConfig(ctx, cfg) + + expectedConfig, err := readTestConfig() + if err != nil { + return err + } + + expectedConfig.LogLevel = "debug" + expectedConfig.Gitea.Root = "not-codeberg.org" + expectedConfig.ACME.AcceptTerms = true + expectedConfig.Server.Host = "172.17.0.2" + expectedConfig.Server.BlacklistedPaths = append(expectedConfig.Server.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...) + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--config-file", "assets/test_config.toml", + "--log-level", "debug", + "--gitea-root", "not-codeberg.org", + "--acme-accept-terms", + "--host", "172.17.0.2", + }, + ) +} + +func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &Config{ + LogLevel: "original", + Server: ServerConfig{ + Host: "original", + Port: 8080, + HttpPort: 80, + HttpServerEnabled: false, + MainDomain: "original", + RawDomain: "original", + PagesBranches: []string{"original"}, + AllowedCorsDomains: []string{"original"}, + BlacklistedPaths: []string{"original"}, + }, + Gitea: GiteaConfig{ + Root: "original", + Token: "original", + LFSEnabled: false, + FollowSymlinks: false, + DefaultMimeType: "original", + ForbiddenMimeTypes: []string{"original"}, + }, + Database: DatabaseConfig{ + Type: "original", + Conn: "original", + }, + ACME: ACMEConfig{ + Email: "original", + APIEndpoint: "original", + AcceptTerms: false, + UseRateLimits: false, + EAB_HMAC: "original", + EAB_KID: "original", + DNSProvider: "original", + AccountConfigFile: "original", + }, + } + + MergeConfig(ctx, cfg) + + expectedConfig := &Config{ + LogLevel: "changed", + Server: ServerConfig{ + Host: "changed", + Port: 8443, + HttpPort: 443, + HttpServerEnabled: true, + MainDomain: "changed", + RawDomain: "changed", + PagesBranches: []string{"changed"}, + AllowedCorsDomains: []string{"changed"}, + BlacklistedPaths: append([]string{"changed"}, ALWAYS_BLACKLISTED_PATHS...), + }, + Gitea: GiteaConfig{ + Root: "changed", + Token: "changed", + LFSEnabled: true, + FollowSymlinks: true, + DefaultMimeType: "changed", + ForbiddenMimeTypes: []string{"changed"}, + }, + Database: DatabaseConfig{ + Type: "changed", + Conn: "changed", + }, + ACME: ACMEConfig{ + Email: "changed", + APIEndpoint: "changed", + AcceptTerms: true, + UseRateLimits: true, + EAB_HMAC: "changed", + EAB_KID: "changed", + DNSProvider: "changed", + AccountConfigFile: "changed", + }, + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--log-level", "changed", + // Server + "--pages-domain", "changed", + "--raw-domain", "changed", + "--allowed-cors-domains", "changed", + "--blacklisted-paths", "changed", + "--pages-branch", "changed", + "--host", "changed", + "--port", "8443", + "--http-port", "443", + "--enable-http-server", + // Gitea + "--gitea-root", "changed", + "--gitea-api-token", "changed", + "--enable-lfs-support", + "--enable-symlink-support", + "--default-mime-type", "changed", + "--forbidden-mime-types", "changed", + // Database + "--db-type", "changed", + "--db-conn", "changed", + // ACME + "--acme-email", "changed", + "--acme-api-endpoint", "changed", + "--acme-accept-terms", + "--acme-use-rate-limits", + "--acme-eab-hmac", "changed", + "--acme-eab-kid", "changed", + "--dns-provider", "changed", + "--acme-account-config", "changed", + }, + ) +} + +func TestMergeServerConfigShouldAddDefaultBlacklistedPathsToBlacklistedPaths(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &ServerConfig{} + mergeServerConfig(ctx, cfg) + + expected := ALWAYS_BLACKLISTED_PATHS + assert.Equal(t, expected, cfg.BlacklistedPaths) + + return nil + }, + []string{}, + ) +} + +func TestMergeServerConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { + for range []uint8{0, 1} { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &ServerConfig{ + Host: "original", + Port: 8080, + HttpPort: 80, + HttpServerEnabled: false, + MainDomain: "original", + RawDomain: "original", + AllowedCorsDomains: []string{"original"}, + BlacklistedPaths: []string{"original"}, + } + + mergeServerConfig(ctx, cfg) + + expectedConfig := &ServerConfig{ + Host: "changed", + Port: 8443, + HttpPort: 443, + HttpServerEnabled: true, + MainDomain: "changed", + RawDomain: "changed", + AllowedCorsDomains: fixArrayFromCtx(ctx, "allowed-cors-domains", []string{"changed"}), + BlacklistedPaths: fixArrayFromCtx(ctx, "blacklisted-paths", append([]string{"changed"}, ALWAYS_BLACKLISTED_PATHS...)), + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--pages-domain", "changed", + "--raw-domain", "changed", + "--allowed-cors-domains", "changed", + "--blacklisted-paths", "changed", + "--host", "changed", + "--port", "8443", + "--http-port", "443", + "--enable-http-server", + }, + ) + } +} + +func TestMergeServerConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { + type testValuePair struct { + args []string + callback func(*ServerConfig) + } + testValuePairs := []testValuePair{ + {args: []string{"--host", "changed"}, callback: func(sc *ServerConfig) { sc.Host = "changed" }}, + {args: []string{"--port", "8443"}, callback: func(sc *ServerConfig) { sc.Port = 8443 }}, + {args: []string{"--http-port", "443"}, callback: func(sc *ServerConfig) { sc.HttpPort = 443 }}, + {args: []string{"--enable-http-server"}, callback: func(sc *ServerConfig) { sc.HttpServerEnabled = true }}, + {args: []string{"--pages-domain", "changed"}, callback: func(sc *ServerConfig) { sc.MainDomain = "changed" }}, + {args: []string{"--raw-domain", "changed"}, callback: func(sc *ServerConfig) { sc.RawDomain = "changed" }}, + {args: []string{"--pages-branch", "changed"}, callback: func(sc *ServerConfig) { sc.PagesBranches = []string{"changed"} }}, + {args: []string{"--allowed-cors-domains", "changed"}, callback: func(sc *ServerConfig) { sc.AllowedCorsDomains = []string{"changed"} }}, + {args: []string{"--blacklisted-paths", "changed"}, callback: func(sc *ServerConfig) { sc.BlacklistedPaths = []string{"changed"} }}, + } + + for _, pair := range testValuePairs { + runApp( + t, + func(ctx *cli.Context) error { + cfg := ServerConfig{ + Host: "original", + Port: 8080, + HttpPort: 80, + HttpServerEnabled: false, + MainDomain: "original", + RawDomain: "original", + PagesBranches: []string{"original"}, + AllowedCorsDomains: []string{"original"}, + BlacklistedPaths: []string{"original"}, + } + + expectedConfig := cfg + pair.callback(&expectedConfig) + expectedConfig.BlacklistedPaths = append(expectedConfig.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...) + + expectedConfig.PagesBranches = fixArrayFromCtx(ctx, "pages-branch", expectedConfig.PagesBranches) + expectedConfig.AllowedCorsDomains = fixArrayFromCtx(ctx, "allowed-cors-domains", expectedConfig.AllowedCorsDomains) + expectedConfig.BlacklistedPaths = fixArrayFromCtx(ctx, "blacklisted-paths", expectedConfig.BlacklistedPaths) + + mergeServerConfig(ctx, &cfg) + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + pair.args, + ) + } +} + +func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &GiteaConfig{ + Root: "original", + Token: "original", + LFSEnabled: false, + FollowSymlinks: false, + DefaultMimeType: "original", + ForbiddenMimeTypes: []string{"original"}, + } + + mergeGiteaConfig(ctx, cfg) + + expectedConfig := &GiteaConfig{ + Root: "changed", + Token: "changed", + LFSEnabled: true, + FollowSymlinks: true, + DefaultMimeType: "changed", + ForbiddenMimeTypes: fixArrayFromCtx(ctx, "forbidden-mime-types", []string{"changed"}), + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--gitea-root", "changed", + "--gitea-api-token", "changed", + "--enable-lfs-support", + "--enable-symlink-support", + "--default-mime-type", "changed", + "--forbidden-mime-types", "changed", + }, + ) +} + +func TestMergeGiteaConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { + type testValuePair struct { + args []string + callback func(*GiteaConfig) + } + testValuePairs := []testValuePair{ + {args: []string{"--gitea-root", "changed"}, callback: func(gc *GiteaConfig) { gc.Root = "changed" }}, + {args: []string{"--gitea-api-token", "changed"}, callback: func(gc *GiteaConfig) { gc.Token = "changed" }}, + {args: []string{"--enable-lfs-support"}, callback: func(gc *GiteaConfig) { gc.LFSEnabled = true }}, + {args: []string{"--enable-symlink-support"}, callback: func(gc *GiteaConfig) { gc.FollowSymlinks = true }}, + {args: []string{"--default-mime-type", "changed"}, callback: func(gc *GiteaConfig) { gc.DefaultMimeType = "changed" }}, + {args: []string{"--forbidden-mime-types", "changed"}, callback: func(gc *GiteaConfig) { gc.ForbiddenMimeTypes = []string{"changed"} }}, + } + + for _, pair := range testValuePairs { + runApp( + t, + func(ctx *cli.Context) error { + cfg := GiteaConfig{ + Root: "original", + Token: "original", + LFSEnabled: false, + FollowSymlinks: false, + DefaultMimeType: "original", + ForbiddenMimeTypes: []string{"original"}, + } + + expectedConfig := cfg + pair.callback(&expectedConfig) + + mergeGiteaConfig(ctx, &cfg) + + expectedConfig.ForbiddenMimeTypes = fixArrayFromCtx(ctx, "forbidden-mime-types", expectedConfig.ForbiddenMimeTypes) + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + pair.args, + ) + } +} + +func TestMergeDatabaseConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &DatabaseConfig{ + Type: "original", + Conn: "original", + } + + mergeDatabaseConfig(ctx, cfg) + + expectedConfig := &DatabaseConfig{ + Type: "changed", + Conn: "changed", + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--db-type", "changed", + "--db-conn", "changed", + }, + ) +} + +func TestMergeDatabaseConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { + type testValuePair struct { + args []string + callback func(*DatabaseConfig) + } + testValuePairs := []testValuePair{ + {args: []string{"--db-type", "changed"}, callback: func(gc *DatabaseConfig) { gc.Type = "changed" }}, + {args: []string{"--db-conn", "changed"}, callback: func(gc *DatabaseConfig) { gc.Conn = "changed" }}, + } + + for _, pair := range testValuePairs { + runApp( + t, + func(ctx *cli.Context) error { + cfg := DatabaseConfig{ + Type: "original", + Conn: "original", + } + + expectedConfig := cfg + pair.callback(&expectedConfig) + + mergeDatabaseConfig(ctx, &cfg) + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + pair.args, + ) + } +} + +func TestMergeACMEConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &ACMEConfig{ + Email: "original", + APIEndpoint: "original", + AcceptTerms: false, + UseRateLimits: false, + EAB_HMAC: "original", + EAB_KID: "original", + DNSProvider: "original", + AccountConfigFile: "original", + } + + mergeACMEConfig(ctx, cfg) + + expectedConfig := &ACMEConfig{ + Email: "changed", + APIEndpoint: "changed", + AcceptTerms: true, + UseRateLimits: true, + EAB_HMAC: "changed", + EAB_KID: "changed", + DNSProvider: "changed", + AccountConfigFile: "changed", + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--acme-email", "changed", + "--acme-api-endpoint", "changed", + "--acme-accept-terms", + "--acme-use-rate-limits", + "--acme-eab-hmac", "changed", + "--acme-eab-kid", "changed", + "--dns-provider", "changed", + "--acme-account-config", "changed", + }, + ) +} + +func TestMergeACMEConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { + type testValuePair struct { + args []string + callback func(*ACMEConfig) + } + testValuePairs := []testValuePair{ + {args: []string{"--acme-email", "changed"}, callback: func(gc *ACMEConfig) { gc.Email = "changed" }}, + {args: []string{"--acme-api-endpoint", "changed"}, callback: func(gc *ACMEConfig) { gc.APIEndpoint = "changed" }}, + {args: []string{"--acme-accept-terms"}, callback: func(gc *ACMEConfig) { gc.AcceptTerms = true }}, + {args: []string{"--acme-use-rate-limits"}, callback: func(gc *ACMEConfig) { gc.UseRateLimits = true }}, + {args: []string{"--acme-eab-hmac", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_HMAC = "changed" }}, + {args: []string{"--acme-eab-kid", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_KID = "changed" }}, + {args: []string{"--dns-provider", "changed"}, callback: func(gc *ACMEConfig) { gc.DNSProvider = "changed" }}, + {args: []string{"--acme-account-config", "changed"}, callback: func(gc *ACMEConfig) { gc.AccountConfigFile = "changed" }}, + } + + for _, pair := range testValuePairs { + runApp( + t, + func(ctx *cli.Context) error { + cfg := ACMEConfig{ + Email: "original", + APIEndpoint: "original", + AcceptTerms: false, + UseRateLimits: false, + EAB_HMAC: "original", + EAB_KID: "original", + DNSProvider: "original", + AccountConfigFile: "original", + } + + expectedConfig := cfg + pair.callback(&expectedConfig) + + mergeACMEConfig(ctx, &cfg) + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + pair.args, + ) + } +} diff --git a/example_config.toml b/example_config.toml new file mode 100644 index 0000000..30e77c4 --- /dev/null +++ b/example_config.toml @@ -0,0 +1,32 @@ +logLevel = 'debug' + +[server] +host = '[::]' +port = 443 +httpPort = 80 +httpServerEnabled = true +mainDomain = 'codeberg.page' +rawDomain = 'raw.codeberg.page' +pagesBranches = ["pages"] +allowedCorsDomains = [] +blacklistedPaths = [] + +[gitea] +root = 'https://codeberg.org' +token = 'ASDF1234' +lfsEnabled = true +followSymlinks = true + +[database] +type = 'sqlite' +conn = 'certs.sqlite' + +[ACME] +email = 'noreply@example.email' +apiEndpoint = 'https://acme-v02.api.letsencrypt.org/directory' +acceptTerms = false +useRateLimits = false +eab_hmac = '' +eab_kid = '' +dnsProvider = '' +accountConfigFile = 'acme-account.json' diff --git a/go.mod b/go.mod index eba292e..47e1e71 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,10 @@ require ( github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 github.com/microcosm-cc/bluemonday v1.0.26 + github.com/pelletier/go-toml/v2 v2.1.0 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.27.0 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb xorm.io/xorm v1.3.2 @@ -44,6 +45,7 @@ require ( github.com/cloudflare/cloudflare-go v0.20.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/creasty/defaults v1.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/deepmap/oapi-codegen v1.6.1 // indirect @@ -113,7 +115,7 @@ require ( github.com/softlayer/softlayer-go v1.0.3 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/spf13/cast v1.3.1 // indirect - github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/transip/gotransip/v6 v6.6.1 // indirect github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect @@ -135,6 +137,6 @@ require ( gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect xorm.io/builder v0.3.12 // indirect ) diff --git a/go.sum b/go.sum index 7ea8b78..1a10599 100644 --- a/go.sum +++ b/go.sum @@ -131,6 +131,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= +github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -570,6 +572,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -680,14 +684,19 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= @@ -1079,8 +1088,9 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/integration/main_test.go b/integration/main_test.go index 6566f78..86fd9d3 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -10,9 +10,10 @@ import ( "testing" "time" - "codeberg.org/codeberg/pages/cmd" - "github.com/urfave/cli/v2" + + cmd "codeberg.org/codeberg/pages/cli" + "codeberg.org/codeberg/pages/server" ) func TestMain(m *testing.M) { @@ -32,10 +33,7 @@ func TestMain(m *testing.M) { } func startServer(ctx context.Context) error { - args := []string{ - "--verbose", - "--acme-accept-terms", "true", - } + args := []string{"integration"} setEnvIfNotSet("ACME_API", "https://acme.mock.directory") setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory") setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory") @@ -44,10 +42,15 @@ func startServer(ctx context.Context) error { setEnvIfNotSet("HTTP_PORT", "8880") setEnvIfNotSet("ENABLE_HTTP_SERVER", "true") setEnvIfNotSet("DB_TYPE", "sqlite3") + setEnvIfNotSet("GITEA_ROOT", "https://codeberg.org") + setEnvIfNotSet("LOG_LEVEL", "trace") + setEnvIfNotSet("ENABLE_LFS_SUPPORT", "true") + setEnvIfNotSet("ENABLE_SYMLINK_SUPPORT", "true") + setEnvIfNotSet("ACME_ACCOUNT_CONFIG", "integration/acme-account.json") app := cli.NewApp() app.Name = "pages-server" - app.Action = cmd.Serve + app.Action = server.Serve app.Flags = cmd.ServerFlags go func() { diff --git a/main.go b/main.go index 6c1d0cc..87e21f3 100644 --- a/main.go +++ b/main.go @@ -1,29 +1,21 @@ package main import ( - "fmt" "os" _ "github.com/joho/godotenv/autoload" - "github.com/urfave/cli/v2" + "github.com/rs/zerolog/log" - "codeberg.org/codeberg/pages/cmd" - "codeberg.org/codeberg/pages/server/version" + "codeberg.org/codeberg/pages/cli" + "codeberg.org/codeberg/pages/server" ) func main() { - app := cli.NewApp() - app.Name = "pages-server" - app.Version = version.Version - app.Usage = "pages server" - app.Action = cmd.Serve - app.Flags = cmd.ServerFlags - app.Commands = []*cli.Command{ - cmd.Certs, - } + app := cli.CreatePagesApp() + app.Action = server.Serve if err := app.Run(os.Args); err != nil { - _, _ = fmt.Fprintln(os.Stderr, err) + log.Error().Err(err).Msg("A fatal error occurred") os.Exit(1) } } diff --git a/server/acme/client.go b/server/acme/client.go new file mode 100644 index 0000000..38e2785 --- /dev/null +++ b/server/acme/client.go @@ -0,0 +1,26 @@ +package acme + +import ( + "errors" + "fmt" + + "codeberg.org/codeberg/pages/config" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/certificates" +) + +var ErrAcmeMissConfig = errors.New("ACME client has wrong config") + +func CreateAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache cache.ICache) (*certificates.AcmeClient, error) { + // check config + if (!cfg.AcceptTerms || cfg.DNSProvider == "") && cfg.APIEndpoint != "https://acme.mock.directory" { + return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) + } + if cfg.EAB_HMAC != "" && cfg.EAB_KID == "" { + return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) + } else if cfg.EAB_HMAC == "" && cfg.EAB_KID != "" { + return nil, fmt.Errorf("%w: ACME_EAB_KID also needs ACME_EAB_HMAC to be set", ErrAcmeMissConfig) + } + + return certificates.NewAcmeClient(cfg, enableHTTPServer, challengeCache) +} diff --git a/server/cache/interface.go b/server/cache/interface.go index 2952b29..b3412cc 100644 --- a/server/cache/interface.go +++ b/server/cache/interface.go @@ -2,7 +2,8 @@ package cache import "time" -type SetGetKey interface { +// ICache is an interface that defines how the pages server interacts with the cache. +type ICache interface { Set(key string, value interface{}, ttl time.Duration) error Get(key string) (interface{}, bool) Remove(key string) diff --git a/server/cache/setup.go b/server/cache/memory.go similarity index 69% rename from server/cache/setup.go rename to server/cache/memory.go index a5928b0..093696f 100644 --- a/server/cache/setup.go +++ b/server/cache/memory.go @@ -2,6 +2,6 @@ package cache import "github.com/OrlovEvgeny/go-mcache" -func NewKeyValueCache() SetGetKey { +func NewInMemoryCache() ICache { return mcache.New() } diff --git a/server/certificates/acme_client.go b/server/certificates/acme_client.go index ba83e50..d53e854 100644 --- a/server/certificates/acme_client.go +++ b/server/certificates/acme_client.go @@ -10,6 +10,7 @@ import ( "github.com/reugn/equalizer" "github.com/rs/zerolog/log" + "codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/server/cache" ) @@ -28,8 +29,8 @@ type AcmeClient struct { acmeClientCertificateLimitPerUser map[string]*equalizer.TokenBucket } -func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, dnsProvider string, acmeAcceptTerms, enableHTTPServer, acmeUseRateLimits bool, challengeCache cache.SetGetKey) (*AcmeClient, error) { - acmeConfig, err := setupAcmeConfig(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, acmeAcceptTerms) +func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache cache.ICache) (*AcmeClient, error) { + acmeConfig, err := setupAcmeConfig(cfg) if err != nil { return nil, err } @@ -54,7 +55,7 @@ func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, if err != nil { log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { - if dnsProvider == "" { + if cfg.DNSProvider == "" { // using mock server, don't use wildcard certs err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) if err != nil { @@ -62,7 +63,7 @@ func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, } } else { // use DNS-Challenge https://go-acme.github.io/lego/dns/ - provider, err := dns.NewDNSChallengeProviderByName(dnsProvider) + provider, err := dns.NewDNSChallengeProviderByName(cfg.DNSProvider) if err != nil { return nil, fmt.Errorf("can not create DNS Challenge provider: %w", err) } @@ -76,7 +77,7 @@ func NewAcmeClient(acmeAccountConf, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID, legoClient: acmeClient, dnsChallengerLegoClient: mainDomainAcmeClient, - acmeUseRateLimits: acmeUseRateLimits, + acmeUseRateLimits: cfg.UseRateLimits, obtainLocks: sync.Map{}, diff --git a/server/certificates/acme_config.go b/server/certificates/acme_config.go index 12ad7c6..2b5151d 100644 --- a/server/certificates/acme_config.go +++ b/server/certificates/acme_config.go @@ -8,6 +8,7 @@ import ( "fmt" "os" + "codeberg.org/codeberg/pages/config" "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/registration" @@ -16,21 +17,27 @@ import ( const challengePath = "/.well-known/acme-challenge/" -func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID string, acmeAcceptTerms bool) (*lego.Config, error) { +func setupAcmeConfig(cfg config.ACMEConfig) (*lego.Config, error) { var myAcmeAccount AcmeAccount var myAcmeConfig *lego.Config - if account, err := os.ReadFile(configFile); err == nil { - log.Info().Msgf("found existing acme account config file '%s'", configFile) + if cfg.AccountConfigFile == "" { + return nil, fmt.Errorf("invalid acme config file: '%s'", cfg.AccountConfigFile) + } + + if account, err := os.ReadFile(cfg.AccountConfigFile); err == nil { + log.Info().Msgf("found existing acme account config file '%s'", cfg.AccountConfigFile) if err := json.Unmarshal(account, &myAcmeAccount); err != nil { return nil, err } + myAcmeAccount.Key, err = certcrypto.ParsePEMPrivateKey([]byte(myAcmeAccount.KeyPEM)) if err != nil { return nil, err } + myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = acmeAPI + myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 // Validate Config @@ -39,6 +46,7 @@ func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID stri log.Info().Err(err).Msg("config validation failed, you might just delete the config file and let it recreate") return nil, fmt.Errorf("acme config validation failed: %w", err) } + return myAcmeConfig, nil } else if !os.IsNotExist(err) { return nil, err @@ -51,20 +59,20 @@ func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID stri return nil, err } myAcmeAccount = AcmeAccount{ - Email: acmeMail, + Email: cfg.Email, Key: privateKey, KeyPEM: string(certcrypto.PEMEncode(privateKey)), } myAcmeConfig = lego.NewConfig(&myAcmeAccount) - myAcmeConfig.CADirURL = acmeAPI + myAcmeConfig.CADirURL = cfg.APIEndpoint myAcmeConfig.Certificate.KeyType = certcrypto.RSA2048 tempClient, err := lego.NewClient(myAcmeConfig) if err != nil { log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { // accept terms & log in to EAB - if acmeEabKID == "" || acmeEabHmac == "" { - reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: acmeAcceptTerms}) + if cfg.EAB_KID == "" || cfg.EAB_HMAC == "" { + reg, err := tempClient.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.AcceptTerms}) if err != nil { log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") } else { @@ -72,9 +80,9 @@ func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID stri } } else { reg, err := tempClient.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{ - TermsOfServiceAgreed: acmeAcceptTerms, - Kid: acmeEabKID, - HmacEncoded: acmeEabHmac, + TermsOfServiceAgreed: cfg.AcceptTerms, + Kid: cfg.EAB_KID, + HmacEncoded: cfg.EAB_HMAC, }) if err != nil { log.Error().Err(err).Msg("Can't register ACME account, continuing with mock certs only") @@ -89,8 +97,8 @@ func setupAcmeConfig(configFile, acmeAPI, acmeMail, acmeEabHmac, acmeEabKID stri log.Error().Err(err).Msg("json.Marshalfailed, waiting for manual restart to avoid rate limits") select {} } - log.Info().Msgf("new acme account created. write to config file '%s'", configFile) - err = os.WriteFile(configFile, acmeAccountJSON, 0o600) + log.Info().Msgf("new acme account created. write to config file '%s'", cfg.AccountConfigFile) + err = os.WriteFile(cfg.AccountConfigFile, acmeAccountJSON, 0o600) if err != nil { log.Error().Err(err).Msg("os.WriteFile failed, waiting for manual restart to avoid rate limits") select {} diff --git a/server/certificates/cached_challengers.go b/server/certificates/cached_challengers.go index bc9ea67..39439fb 100644 --- a/server/certificates/cached_challengers.go +++ b/server/certificates/cached_challengers.go @@ -15,7 +15,7 @@ import ( ) type AcmeTLSChallengeProvider struct { - challengeCache cache.SetGetKey + challengeCache cache.ICache } // make sure AcmeTLSChallengeProvider match Provider interface @@ -31,7 +31,7 @@ func (a AcmeTLSChallengeProvider) CleanUp(domain, _, _ string) error { } type AcmeHTTPChallengeProvider struct { - challengeCache cache.SetGetKey + challengeCache cache.ICache } // make sure AcmeHTTPChallengeProvider match Provider interface @@ -46,7 +46,7 @@ func (a AcmeHTTPChallengeProvider) CleanUp(domain, token, _ string) error { return nil } -func SetupHTTPACMEChallengeServer(challengeCache cache.SetGetKey, sslPort uint) http.HandlerFunc { +func SetupHTTPACMEChallengeServer(challengeCache cache.ICache, sslPort uint) http.HandlerFunc { // handle custom-ssl-ports to be added on https redirects portPart := "" if sslPort != 443 { diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 4fb99fa..b638755 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -31,7 +31,7 @@ func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, acmeClient *AcmeClient, firstDefaultBranch string, - keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.SetGetKey, + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.ICache, certDB database.CertDB, ) *tls.Config { return &tls.Config{ diff --git a/server/dns/dns.go b/server/dns/dns.go index c11b278..970f0c0 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -15,7 +15,7 @@ var defaultPagesRepo = "pages" // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.SetGetKey) (targetOwner, targetRepo, targetBranch string) { +func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.ICache) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error diff --git a/server/gitea/cache.go b/server/gitea/cache.go index af61edf..267c3d8 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -74,7 +74,7 @@ type writeCacheReader struct { buffer *bytes.Buffer rileResponse *FileResponse cacheKey string - cache cache.SetGetKey + cache cache.ICache hasError bool } @@ -99,7 +99,7 @@ func (t *writeCacheReader) Close() error { return t.originalReader.Close() } -func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.SetGetKey, cacheKey string) io.ReadCloser { +func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser { if r == nil || cache == nil || cacheKey == "" { log.Error().Msg("could not create CacheReader") return nil diff --git a/server/gitea/client.go b/server/gitea/client.go index f3bda54..42cf065 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/sdk/gitea" "github.com/rs/zerolog/log" + "codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/version" ) @@ -44,7 +45,7 @@ const ( type Client struct { sdkClient *gitea.Client - responseCache cache.SetGetKey + responseCache cache.ICache giteaRoot string @@ -55,24 +56,21 @@ type Client struct { defaultMimeType string } -func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, followSymlinks, supportLFS bool) (*Client, error) { - rootURL, err := url.Parse(giteaRoot) +func NewClient(cfg config.GiteaConfig, respCache cache.ICache) (*Client, error) { + rootURL, err := url.Parse(cfg.Root) if err != nil { return nil, err } - giteaRoot = strings.Trim(rootURL.String(), "/") + giteaRoot := strings.Trim(rootURL.String(), "/") stdClient := http.Client{Timeout: 10 * time.Second} - // TODO: pass down - var ( - forbiddenMimeTypes map[string]bool - defaultMimeType string - ) - - if forbiddenMimeTypes == nil { - forbiddenMimeTypes = make(map[string]bool) + forbiddenMimeTypes := make(map[string]bool, len(cfg.ForbiddenMimeTypes)) + for _, mimeType := range cfg.ForbiddenMimeTypes { + forbiddenMimeTypes[mimeType] = true } + + defaultMimeType := cfg.DefaultMimeType if defaultMimeType == "" { defaultMimeType = "application/octet-stream" } @@ -80,7 +78,7 @@ func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, follo sdk, err := gitea.NewClient( giteaRoot, gitea.SetHTTPClient(&stdClient), - gitea.SetToken(giteaAPIToken), + gitea.SetToken(cfg.Token), gitea.SetUserAgent("pages-server/"+version.Version), ) @@ -90,8 +88,8 @@ func NewClient(giteaRoot, giteaAPIToken string, respCache cache.SetGetKey, follo giteaRoot: giteaRoot, - followSymlinks: followSymlinks, - supportLFS: supportLFS, + followSymlinks: cfg.FollowSymlinks, + supportLFS: cfg.LFSEnabled, forbiddenMimeTypes: forbiddenMimeTypes, defaultMimeType: defaultMimeType, diff --git a/server/handler/handler.go b/server/handler/handler.go index 7da5d39..96788e4 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -6,6 +6,7 @@ import ( "github.com/rs/zerolog/log" + "codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/html" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/context" @@ -19,11 +20,10 @@ const ( ) // Handler handles a single HTTP request to the web server. -func Handler(mainDomainSuffix, rawDomain string, +func Handler( + cfg config.ServerConfig, giteaClient *gitea.Client, - blacklistedPaths, allowedCorsDomains []string, - defaultPagesBranches []string, - dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey, + dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() @@ -39,8 +39,8 @@ func Handler(mainDomainSuffix, rawDomain string, trimmedHost := ctx.TrimHostPort() - // Add HSTS for RawDomain and MainDomainSuffix - if hsts := getHSTSHeader(trimmedHost, mainDomainSuffix, rawDomain); hsts != "" { + // Add HSTS for RawDomain and MainDomain + if hsts := getHSTSHeader(trimmedHost, cfg.MainDomain, cfg.RawDomain); hsts != "" { ctx.RespWriter.Header().Set("Strict-Transport-Security", hsts) } @@ -62,7 +62,7 @@ func Handler(mainDomainSuffix, rawDomain string, } // Block blacklisted paths (like ACME challenges) - for _, blacklistedPath := range blacklistedPaths { + for _, blacklistedPath := range cfg.BlacklistedPaths { if strings.HasPrefix(ctx.Path(), blacklistedPath) { html.ReturnErrorPage(ctx, "requested path is blacklisted", http.StatusForbidden) return @@ -71,7 +71,7 @@ func Handler(mainDomainSuffix, rawDomain string, // Allow CORS for specified domains allowCors := false - for _, allowedCorsDomain := range allowedCorsDomains { + for _, allowedCorsDomain := range cfg.AllowedCorsDomains { if strings.EqualFold(trimmedHost, allowedCorsDomain) { allowCors = true break @@ -85,28 +85,28 @@ func Handler(mainDomainSuffix, rawDomain string, // Prepare request information to Gitea pathElements := strings.Split(strings.Trim(ctx.Path(), "/"), "/") - if rawDomain != "" && strings.EqualFold(trimmedHost, rawDomain) { + if cfg.RawDomain != "" && strings.EqualFold(trimmedHost, cfg.RawDomain) { log.Debug().Msg("raw domain request detected") handleRaw(log, ctx, giteaClient, - mainDomainSuffix, + cfg.MainDomain, trimmedHost, pathElements, canonicalDomainCache, redirectsCache) - } else if strings.HasSuffix(trimmedHost, mainDomainSuffix) { + } else if strings.HasSuffix(trimmedHost, cfg.MainDomain) { log.Debug().Msg("subdomain request detected") handleSubDomain(log, ctx, giteaClient, - mainDomainSuffix, - defaultPagesBranches, + cfg.MainDomain, + cfg.PagesBranches, trimmedHost, pathElements, canonicalDomainCache, redirectsCache) } else { log.Debug().Msg("custom domain request detected") handleCustomDomain(log, ctx, giteaClient, - mainDomainSuffix, + cfg.MainDomain, trimmedHost, pathElements, - defaultPagesBranches[0], + cfg.PagesBranches[0], dnsLookupCache, canonicalDomainCache, redirectsCache) } } diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 8742be4..82953f9 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -19,7 +19,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g trimmedHost string, pathElements []string, firstDefaultBranch string, - dnsLookupCache, canonicalDomainCache, redirectsCache cache.SetGetKey, + dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, ) { // Serve pages from custom domains targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go index caa8209..f48e8e4 100644 --- a/server/handler/handler_raw_domain.go +++ b/server/handler/handler_raw_domain.go @@ -19,7 +19,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie mainDomainSuffix string, trimmedHost string, pathElements []string, - canonicalDomainCache, redirectsCache cache.SetGetKey, + canonicalDomainCache, redirectsCache cache.ICache, ) { // Serve raw content from RawDomain log.Debug().Msg("raw domain") diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 6c14393..806fe7f 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -21,7 +21,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite defaultPagesBranches []string, trimmedHost string, pathElements []string, - canonicalDomainCache, redirectsCache cache.SetGetKey, + canonicalDomainCache, redirectsCache cache.ICache, ) { // Serve pages from subdomains of MainDomainSuffix log.Debug().Msg("main domain suffix") diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index d04ebda..4cb859a 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -6,23 +6,30 @@ import ( "testing" "time" + "codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/server/cache" "codeberg.org/codeberg/pages/server/gitea" "github.com/rs/zerolog/log" ) func TestHandlerPerformance(t *testing.T) { - giteaClient, _ := gitea.NewClient("https://codeberg.org", "", cache.NewKeyValueCache(), false, false) - testHandler := Handler( - "codeberg.page", "raw.codeberg.org", - giteaClient, - []string{"/.well-known/acme-challenge/"}, - []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, - []string{"pages"}, - cache.NewKeyValueCache(), - cache.NewKeyValueCache(), - cache.NewKeyValueCache(), - ) + cfg := config.GiteaConfig{ + Root: "https://codeberg.org", + Token: "", + LFSEnabled: false, + FollowSymlinks: false, + } + giteaClient, _ := gitea.NewClient(cfg, cache.NewInMemoryCache()) + serverCfg := config.ServerConfig{ + MainDomain: "codeberg.page", + RawDomain: "raw.codeberg.page", + BlacklistedPaths: []string{ + "/.well-known/acme-challenge/", + }, + AllowedCorsDomains: []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, + PagesBranches: []string{"pages"}, + } + testHandler := Handler(serverCfg, giteaClient, cache.NewInMemoryCache(), cache.NewInMemoryCache(), cache.NewInMemoryCache()) testCase := func(uri string, status int) { t.Run(uri, func(t *testing.T) { diff --git a/server/handler/try.go b/server/handler/try.go index bb79dce..145b1a9 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -17,8 +17,8 @@ import ( func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix, trimmedHost string, options *upstream.Options, - canonicalDomainCache cache.SetGetKey, - redirectsCache cache.SetGetKey, + canonicalDomainCache cache.ICache, + redirectsCache cache.ICache, ) { // check if a canonical domain exists on a request on MainDomain if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw { diff --git a/server/startup.go b/server/startup.go new file mode 100644 index 0000000..ffdabb7 --- /dev/null +++ b/server/startup.go @@ -0,0 +1,141 @@ +package server + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "net" + "net/http" + "os" + "strings" + "time" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/urfave/cli/v2" + + cmd "codeberg.org/codeberg/pages/cli" + "codeberg.org/codeberg/pages/config" + "codeberg.org/codeberg/pages/server/acme" + "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/certificates" + "codeberg.org/codeberg/pages/server/gitea" + "codeberg.org/codeberg/pages/server/handler" +) + +// Serve sets up and starts the web server. +func Serve(ctx *cli.Context) error { + // initialize logger with Trace, overridden later with actual level + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(zerolog.TraceLevel) + + cfg, err := config.ReadConfig(ctx) + if err != nil { + log.Error().Err(err).Msg("could not read config") + } + + config.MergeConfig(ctx, cfg) + + // Initialize the logger. + logLevel, err := zerolog.ParseLevel(cfg.LogLevel) + if err != nil { + return err + } + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) + + foo, _ := json.Marshal(cfg) + log.Trace().RawJSON("config", foo).Msg("starting server with config") + + listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) + listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort) + + if cfg.Server.RawDomain != "" { + cfg.Server.AllowedCorsDomains = append(cfg.Server.AllowedCorsDomains, cfg.Server.RawDomain) + } + + // Make sure MainDomain has a leading dot + if !strings.HasPrefix(cfg.Server.MainDomain, ".") { + // TODO make this better + cfg.Server.MainDomain = "." + cfg.Server.MainDomain + } + + if len(cfg.Server.PagesBranches) == 0 { + return fmt.Errorf("no default branches set (PAGES_BRANCHES)") + } + + // Init ssl cert database + certDB, closeFn, err := cmd.OpenCertDB(ctx) + if err != nil { + return err + } + defer closeFn() + + keyCache := cache.NewInMemoryCache() + challengeCache := cache.NewInMemoryCache() + // canonicalDomainCache stores canonical domains + canonicalDomainCache := cache.NewInMemoryCache() + // dnsLookupCache stores DNS lookups for custom domains + dnsLookupCache := cache.NewInMemoryCache() + // redirectsCache stores redirects in _redirects files + redirectsCache := cache.NewInMemoryCache() + // clientResponseCache stores responses from the Gitea server + clientResponseCache := cache.NewInMemoryCache() + + giteaClient, err := gitea.NewClient(cfg.Gitea, clientResponseCache) + if err != nil { + return fmt.Errorf("could not create new gitea client: %v", err) + } + + acmeClient, err := acme.CreateAcmeClient(cfg.ACME, cfg.Server.HttpServerEnabled, challengeCache) + if err != nil { + return err + } + + if err := certificates.SetupMainDomainCertificates(cfg.Server.MainDomain, acmeClient, certDB); err != nil { + return err + } + + // Create listener for SSL connections + log.Info().Msgf("Create TCP listener for SSL on %s", listeningSSLAddress) + listener, err := net.Listen("tcp", listeningSSLAddress) + if err != nil { + return fmt.Errorf("couldn't create listener: %v", err) + } + + // Setup listener for SSL connections + listener = tls.NewListener(listener, certificates.TLSConfig( + cfg.Server.MainDomain, + giteaClient, + acmeClient, + cfg.Server.PagesBranches[0], + keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, + certDB, + )) + + interval := 12 * time.Hour + certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) + defer cancelCertMaintain() + go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB) + + if cfg.Server.HttpServerEnabled { + // Create handler for http->https redirect and http acme challenges + httpHandler := certificates.SetupHTTPACMEChallengeServer(challengeCache, uint(cfg.Server.Port)) + + // Create listener for http and start listening + go func() { + log.Info().Msgf("Start HTTP server listening on %s", listeningHTTPAddress) + err := http.ListenAndServe(listeningHTTPAddress, httpHandler) + if err != nil { + log.Error().Err(err).Msg("Couldn't start HTTP server") + } + }() + } + + // Create ssl handler based on settings + sslHandler := handler.Handler(cfg.Server, giteaClient, dnsLookupCache, canonicalDomainCache, redirectsCache) + + // Start the ssl listener + log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) + + return http.Serve(listener, sslHandler) +} diff --git a/server/upstream/domains.go b/server/upstream/domains.go index 230f268..d53d586 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -17,7 +17,7 @@ var canonicalDomainCacheTimeout = 15 * time.Minute const canonicalDomainConfig = ".domains" // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). -func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.SetGetKey) (domain string, valid bool) { +func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) { // Check if this request is cached. if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { domains := cachedValue.([]string) diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index ab6c971..dd36a84 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -23,7 +23,7 @@ var redirectsCacheTimeout = 10 * time.Minute const redirectsConfig = "_redirects" // getRedirects returns redirects specified in the _redirects file. -func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.SetGetKey) []Redirect { +func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect { var redirects []Redirect cacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch @@ -63,7 +63,7 @@ func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.S return redirects } -func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client, redirects []Redirect, redirectsCache cache.SetGetKey) (final bool) { +func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client, redirects []Redirect, redirectsCache cache.ICache) (final bool) { if len(redirects) > 0 { for _, redirect := range redirects { reqUrl := ctx.Req.RequestURI diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 4a85503..07b6ad2 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -53,7 +53,7 @@ type Options struct { } // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. -func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.SetGetKey) bool { +func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) bool { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() if o.TargetOwner == "" || o.TargetRepo == "" { From a6e9510c078758794da5f6a1983b595bfc415de9 Mon Sep 17 00:00:00 2001 From: Hoernschen Date: Mon, 26 Feb 2024 22:21:42 +0000 Subject: [PATCH 221/320] FIX blank internal pages (#164) (#292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello 👋 since it affected my deployment of the pages server I started to look into the problem of the blank pages and think I found a solution for it: 1. There is no check if the file response is empty, neither in cache retrieval nor in writing of a cache. Also the provided method for checking for empty responses had a bug. 2. I identified the redirect response to be the issue here. There is a cache write with the full cache key (e. g. rawContent/user/repo|branch|route/index.html) happening in the handling of the redirect response. But the written body here is empty. In the triggered request from the redirect response the server then finds a cache item to the key and serves the empty body. A quick fix is the check for empty file responses mentioned in 1. 3. The decision to redirect the user comes quite far down in the upstream function. Before that happens a lot of stuff that may not be important since after the redirect response comes a new request anyway. Also, I suspect that this causes the caching problem because there is a request to the forge server and its error handling with some recursions happening before. I propose to move two of the redirects before "Preparing" 4. The recursion in the upstream function makes it difficult to understand what is actually happening. I added some more logging to have an easier time with that. 5. I changed the default behaviour to append a trailing slash to the path to true. In my tested scenarios it happened anyway. This way there is no recursion happening before the redirect. I am not developing in go frequently and rarely contribute to open source -> so feedback of all kind is appreciated closes #164 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/292 Reviewed-by: 6543 <6543@obermui.de> Reviewed-by: crapStone Co-authored-by: Hoernschen Co-committed-by: Hoernschen --- server/gitea/cache.go | 25 +++++++++++++++++-------- server/gitea/client.go | 19 +++++++++++-------- server/handler/handler.go | 1 + server/upstream/upstream.go | 12 ++++++++++++ 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 267c3d8..4ecc5e0 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -39,7 +39,7 @@ type FileResponse struct { } func (f FileResponse) IsEmpty() bool { - return len(f.Body) != 0 + return len(f.Body) == 0 } func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) { @@ -72,13 +72,14 @@ type BranchTimestamp struct { type writeCacheReader struct { originalReader io.ReadCloser buffer *bytes.Buffer - rileResponse *FileResponse + fileResponse *FileResponse cacheKey string cache cache.ICache hasError bool } func (t *writeCacheReader) Read(p []byte) (n int, err error) { + log.Trace().Msgf("[cache] read %q", t.cacheKey) n, err = t.originalReader.Read(p) if err != nil && err != io.EOF { log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) @@ -90,12 +91,20 @@ func (t *writeCacheReader) Read(p []byte) (n int, err error) { } func (t *writeCacheReader) Close() error { - if !t.hasError { - fc := *t.rileResponse - fc.Body = t.buffer.Bytes() - _ = t.cache.Set(t.cacheKey, fc, fileCacheTimeout) + doWrite := !t.hasError + fc := *t.fileResponse + fc.Body = t.buffer.Bytes() + if fc.IsEmpty() { + log.Trace().Msg("[cache] file response is empty") + doWrite = false } - log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, !t.hasError) + if doWrite { + err := t.cache.Set(t.cacheKey, fc, fileCacheTimeout) + if err != nil { + log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey) + } + } + log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite) return t.originalReader.Close() } @@ -108,7 +117,7 @@ func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cac return &writeCacheReader{ originalReader: r, buffer: bytes.NewBuffer(make([]byte, 0)), - rileResponse: &f, + fileResponse: &f, cache: cache, cacheKey: cacheKey, } diff --git a/server/gitea/client.go b/server/gitea/client.go index 42cf065..4f7eaa6 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -112,26 +112,27 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) { cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) log := log.With().Str("cache_key", cacheKey).Logger() - + log.Trace().Msg("try file in cache") // handle if cache entry exist if cache, ok := client.responseCache.Get(cacheKey); ok { cache := cache.(FileResponse) cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) // TODO: check against some timestamp mismatch?!? if cache.Exists { + log.Debug().Msg("[cache] exists") if cache.IsSymlink { linkDest := string(cache.Body) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) - } else { - log.Debug().Msg("[cache] return bytes") + } else if !cache.IsEmpty() { + log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil + } else if cache.IsEmpty() { + log.Debug().Msg("[cache] is empty") } - } else { - return nil, cachedHeader, cachedStatusCode, ErrorNotFound } } - + log.Trace().Msg("file not in cache") // not in cache, open reader via gitea api reader, resp, err := client.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS) if resp != nil { @@ -155,12 +156,14 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str linkDest = path.Join(path.Dir(resource), linkDest) // we store symlink not content to reduce duplicates in cache - if err := client.responseCache.Set(cacheKey, FileResponse{ + fileResponse := FileResponse{ Exists: true, IsSymlink: true, Body: []byte(linkDest), ETag: resp.Header.Get(ETagHeader), - }, fileCacheTimeout); err != nil { + } + log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body)) + if err := client.responseCache.Set(cacheKey, fileResponse, fileCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } diff --git a/server/handler/handler.go b/server/handler/handler.go index 96788e4..ffc3400 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -26,6 +26,7 @@ func Handler( dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { + log.Debug().Msg("\n----------------------------------------------------------") log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() ctx := context.New(w, req) diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 07b6ad2..d9c131e 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -56,6 +56,8 @@ type Options struct { func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) bool { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() + log.Debug().Msg("Start") + if o.TargetOwner == "" || o.TargetRepo == "" { html.ReturnErrorPage(ctx, "forge client: either repo owner or name info is missing", http.StatusBadRequest) return true @@ -104,13 +106,16 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // Handle not found error if err != nil && errors.Is(err, gitea.ErrorNotFound) { + log.Debug().Msg("Handling not found error") // Get and match redirects redirects := o.getRedirects(giteaClient, redirectsCache) if o.matchRedirects(ctx, giteaClient, redirects, redirectsCache) { + log.Trace().Msg("redirect") return true } if o.TryIndexPages { + log.Trace().Msg("try index page") // copy the o struct & try if an index page exists optionsForIndexPages := *o optionsForIndexPages.TryIndexPages = false @@ -121,6 +126,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi return true } } + log.Trace().Msg("try html file with path name") // compatibility fix for GitHub Pages (/example → /example.html) optionsForIndexPages.appendTrailingSlash = false optionsForIndexPages.redirectIfExists = strings.TrimSuffix(ctx.Path(), "/") + ".html" @@ -130,8 +136,11 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi } } + log.Trace().Msg("not found") + ctx.StatusCode = http.StatusNotFound if o.TryIndexPages { + log.Trace().Msg("try not found page") // copy the o struct & try if a not found page exists optionsForNotFoundPages := *o optionsForNotFoundPages.TryIndexPages = false @@ -142,6 +151,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi return true } } + log.Trace().Msg("not found page missing") } return false @@ -173,10 +183,12 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // Append trailing slash if missing (for index files), and redirect to fix filenames in general // o.appendTrailingSlash is only true when looking for index pages if o.appendTrailingSlash && !strings.HasSuffix(ctx.Path(), "/") { + log.Trace().Msg("append trailing slash and redirect") ctx.Redirect(ctx.Path()+"/", http.StatusTemporaryRedirect) return true } if strings.HasSuffix(ctx.Path(), "/index.html") && !o.ServeRaw { + log.Trace().Msg("remove index.html from path and redirect") ctx.Redirect(strings.TrimSuffix(ctx.Path(), "index.html"), http.StatusTemporaryRedirect) return true } From dd6d8bd60fe0e318602042d1ae8129654f4f5186 Mon Sep 17 00:00:00 2001 From: caelandb Date: Tue, 12 Mar 2024 21:50:17 +0000 Subject: [PATCH 222/320] fixed one grammar error. (#297) Just a small grammar change in the README. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/297 Reviewed-by: crapStone Co-authored-by: caelandb Co-committed-by: caelandb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb2a4b9..7193691 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Thank you very much. Make sure you have [golang](https://go.dev) v1.21 or newer and [just](https://just.systems/man/en/) installed. run `just dev` -now this pages should work: +now these pages should work: - - From 03881382a4c286e3a7dc351f095f9147e89c2d6d Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Thu, 18 Apr 2024 17:05:20 +0000 Subject: [PATCH 223/320] Add option to disable DNS ACME provider (#290) This PR add the `$NO_DNS_01` option (disabled by default) that removes the DNS ACME provider, and replaces the wildcard certificate by individual certificates obtained using the TLS ACME provider. This option allows an instance to work without having to manage access tokens for the DNS provider. On the flip side, this means that a certificate can be requested for each subdomains. To limit the risk of DOS, the existence of the user/org corresponding to a subdomain is checked before requesting a cert, however, this limitation is not enough for an forge with a high number of users/orgs. Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/290 Reviewed-by: Moritz Marquardt Co-authored-by: Jean-Marie 'Histausse' Mineau Co-committed-by: Jean-Marie 'Histausse' Mineau --- README.md | 1 + cli/flags.go | 5 +++++ config/config.go | 1 + config/setup.go | 3 +++ config/setup_test.go | 7 ++++++ server/acme/client.go | 4 ++-- server/certificates/acme_client.go | 7 ++---- server/certificates/certificates.go | 30 ++++++++++++++++++++------ server/database/xorm.go | 13 ------------ server/gitea/cache.go | 3 +++ server/gitea/client.go | 33 +++++++++++++++++++++++++++++ server/startup.go | 2 ++ 12 files changed, 83 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7193691..f47a196 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See for available values & additional environment variables. +- `NO_DNS_01` (default: `false`): Disable the use of ACME DNS. This means that the wildcard certificate is self-signed and all domains and subdomains will have a distinct certificate. Because this may lead to a rate limit from the ACME provider, this option is not recommended for Gitea/Forgejo instances with open registrations or a great number of users/orgs. - `LOG_LEVEL` (default: warn): Set this to specify the level of logging. ## Contributing to the development diff --git a/cli/flags.go b/cli/flags.go index 097cf4f..52e1c1c 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -178,6 +178,11 @@ var ( Usage: "Use DNS-Challenge for main domain. Read more at: https://go-acme.github.io/lego/dns/", EnvVars: []string{"DNS_PROVIDER"}, }, + &cli.BoolFlag{ + Name: "no-dns-01", + Usage: "Always use individual certificates instead of a DNS-01 wild card certificate", + EnvVars: []string{"NO_DNS_01"}, + }, &cli.StringFlag{ Name: "acme-account-config", Usage: "json file of acme account", diff --git a/config/config.go b/config/config.go index 6cb972b..0146e0f 100644 --- a/config/config.go +++ b/config/config.go @@ -42,5 +42,6 @@ type ACMEConfig struct { EAB_HMAC string EAB_KID string DNSProvider string + NoDNS01 bool `default:"false"` AccountConfigFile string `default:"acme-account.json"` } diff --git a/config/setup.go b/config/setup.go index e774084..6a2aa62 100644 --- a/config/setup.go +++ b/config/setup.go @@ -141,6 +141,9 @@ func mergeACMEConfig(ctx *cli.Context, config *ACMEConfig) { if ctx.IsSet("dns-provider") { config.DNSProvider = ctx.String("dns-provider") } + if ctx.IsSet("no-dns-01") { + config.NoDNS01 = ctx.Bool("no-dns-01") + } if ctx.IsSet("acme-account-config") { config.AccountConfigFile = ctx.String("acme-account-config") } diff --git a/config/setup_test.go b/config/setup_test.go index a863e2f..1a32740 100644 --- a/config/setup_test.go +++ b/config/setup_test.go @@ -166,6 +166,7 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T EAB_HMAC: "original", EAB_KID: "original", DNSProvider: "original", + NoDNS01: false, AccountConfigFile: "original", }, } @@ -205,6 +206,7 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T EAB_HMAC: "changed", EAB_KID: "changed", DNSProvider: "changed", + NoDNS01: true, AccountConfigFile: "changed", }, } @@ -243,6 +245,7 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T "--acme-eab-hmac", "changed", "--acme-eab-kid", "changed", "--dns-provider", "changed", + "--no-dns-01", "--acme-account-config", "changed", }, ) @@ -517,6 +520,7 @@ func TestMergeACMEConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testi EAB_HMAC: "original", EAB_KID: "original", DNSProvider: "original", + NoDNS01: false, AccountConfigFile: "original", } @@ -530,6 +534,7 @@ func TestMergeACMEConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testi EAB_HMAC: "changed", EAB_KID: "changed", DNSProvider: "changed", + NoDNS01: true, AccountConfigFile: "changed", } @@ -545,6 +550,7 @@ func TestMergeACMEConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testi "--acme-eab-hmac", "changed", "--acme-eab-kid", "changed", "--dns-provider", "changed", + "--no-dns-01", "--acme-account-config", "changed", }, ) @@ -563,6 +569,7 @@ func TestMergeACMEConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExi {args: []string{"--acme-eab-hmac", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_HMAC = "changed" }}, {args: []string{"--acme-eab-kid", "changed"}, callback: func(gc *ACMEConfig) { gc.EAB_KID = "changed" }}, {args: []string{"--dns-provider", "changed"}, callback: func(gc *ACMEConfig) { gc.DNSProvider = "changed" }}, + {args: []string{"--no-dns-01"}, callback: func(gc *ACMEConfig) { gc.NoDNS01 = true }}, {args: []string{"--acme-account-config", "changed"}, callback: func(gc *ACMEConfig) { gc.AccountConfigFile = "changed" }}, } diff --git a/server/acme/client.go b/server/acme/client.go index 38e2785..d5c83d0 100644 --- a/server/acme/client.go +++ b/server/acme/client.go @@ -13,8 +13,8 @@ var ErrAcmeMissConfig = errors.New("ACME client has wrong config") func CreateAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache cache.ICache) (*certificates.AcmeClient, error) { // check config - if (!cfg.AcceptTerms || cfg.DNSProvider == "") && cfg.APIEndpoint != "https://acme.mock.directory" { - return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) + if (!cfg.AcceptTerms || (cfg.DNSProvider == "" && !cfg.NoDNS01)) && cfg.APIEndpoint != "https://acme.mock.directory" { + return nil, fmt.Errorf("%w: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER or $NO_DNS_01, unless $ACME_API is set to https://acme.mock.directory", ErrAcmeMissConfig) } if cfg.EAB_HMAC != "" && cfg.EAB_KID == "" { return nil, fmt.Errorf("%w: ACME_EAB_HMAC also needs ACME_EAB_KID to be set", ErrAcmeMissConfig) diff --git a/server/certificates/acme_client.go b/server/certificates/acme_client.go index d53e854..f42fd8f 100644 --- a/server/certificates/acme_client.go +++ b/server/certificates/acme_client.go @@ -56,11 +56,8 @@ func NewAcmeClient(cfg config.ACMEConfig, enableHTTPServer bool, challengeCache log.Error().Err(err).Msg("Can't create ACME client, continuing with mock certs only") } else { if cfg.DNSProvider == "" { - // using mock server, don't use wildcard certs - err := mainDomainAcmeClient.Challenge.SetTLSALPN01Provider(AcmeTLSChallengeProvider{challengeCache}) - if err != nil { - log.Error().Err(err).Msg("Can't create TLS-ALPN-01 provider") - } + // using mock wildcard certs + mainDomainAcmeClient = nil } else { // use DNS-Challenge https://go-acme.github.io/lego/dns/ provider, err := dns.NewDNSChallengeProviderByName(cfg.DNSProvider) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index b638755..67219dd 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -33,6 +33,8 @@ func TLSConfig(mainDomainSuffix string, firstDefaultBranch string, keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.ICache, certDB database.CertDB, + noDNS01 bool, + rawDomain string, ) *tls.Config { return &tls.Config{ // check DNS name & get certificate from Let's Encrypt @@ -64,9 +66,24 @@ func TLSConfig(mainDomainSuffix string, targetOwner := "" mayObtainCert := true + if strings.HasSuffix(domain, mainDomainSuffix) || strings.EqualFold(domain, mainDomainSuffix[1:]) { - // deliver default certificate for the main domain (*.codeberg.page) - domain = mainDomainSuffix + if noDNS01 { + // Limit the domains allowed to request a certificate to pages-server domains + // and domains for an existing user of org + if !strings.EqualFold(domain, mainDomainSuffix[1:]) && !strings.EqualFold(domain, rawDomain) { + targetOwner := strings.TrimSuffix(domain, mainDomainSuffix) + owner_exist, err := giteaClient.GiteaCheckIfOwnerExists(targetOwner) + mayObtainCert = owner_exist + if err != nil { + log.Error().Err(err).Msgf("Failed to check '%s' existence on the forge: %s", targetOwner, err) + mayObtainCert = false + } + } + } else { + // deliver default certificate for the main domain (*.codeberg.page) + domain = mainDomainSuffix + } } else { var targetRepo, targetBranch string targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) @@ -199,9 +216,6 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") - if useDnsProvider && domains[0] != "" && domains[0][0] == '*' { - domains = domains[1:] - } // lock to avoid simultaneous requests _, working := c.obtainLocks.LoadOrStore(name, struct{}{}) @@ -219,7 +233,11 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew defer c.obtainLocks.Delete(name) if acmeClient == nil { - return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase) + if useDnsProvider { + return mockCert(domains[0], "DNS ACME client is not defined", mainDomainSuffix, keyDatabase) + } else { + return mockCert(domains[0], "ACME client uninitialized. This is a server error, please report!", mainDomainSuffix, keyDatabase) + } } // request actual cert diff --git a/server/database/xorm.go b/server/database/xorm.go index 217b6d1..63fa39e 100644 --- a/server/database/xorm.go +++ b/server/database/xorm.go @@ -52,7 +52,6 @@ func (x xDB) Close() error { func (x xDB) Put(domain string, cert *certificate.Resource) error { log.Trace().Str("domain", cert.Domain).Msg("inserting cert to db") - domain = integrationTestReplacements(domain) c, err := toCert(domain, cert) if err != nil { return err @@ -82,7 +81,6 @@ func (x xDB) Get(domain string) (*certificate.Resource, error) { if domain[:1] == "." { domain = "*" + domain } - domain = integrationTestReplacements(domain) cert := new(Cert) log.Trace().Str("domain", domain).Msg("get cert from db") @@ -99,7 +97,6 @@ func (x xDB) Delete(domain string) error { if domain[:1] == "." { domain = "*" + domain } - domain = integrationTestReplacements(domain) log.Trace().Str("domain", domain).Msg("delete cert from db") _, err := x.engine.ID(domain).Delete(new(Cert)) @@ -139,13 +136,3 @@ func supportedDriver(driver string) bool { return false } } - -// integrationTestReplacements is needed because integration tests use a single domain cert, -// while production use a wildcard cert -// TODO: find a better way to handle this -func integrationTestReplacements(domainKey string) string { - if domainKey == "*.localhost.mock.directory" { - return "localhost.mock.directory" - } - return domainKey -} diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 4ecc5e0..97024a1 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -26,6 +26,9 @@ const ( // TODO: move as option into cache interface fileCacheTimeout = 5 * time.Minute + // ownerExistenceCacheTimeout specifies the timeout for the existence of a repo/org + ownerExistenceCacheTimeout = 5 * time.Minute + // fileCacheSizeLimit limits the maximum file size that will be cached, and is set to 1 MB by default. fileCacheSizeLimit = int64(1000 * 1000) ) diff --git a/server/gitea/client.go b/server/gitea/client.go index 4f7eaa6..5955bfb 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -28,6 +28,7 @@ const ( branchTimestampCacheKeyPrefix = "branchTime" defaultBranchCacheKeyPrefix = "defaultBranch" rawContentCacheKeyPrefix = "rawContent" + ownerExistenceKeyPrefix = "ownerExist" // pages server PagesCacheIndicatorHeader = "X-Pages-Cache" @@ -266,6 +267,38 @@ func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (str return branch, nil } +func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) { + cacheKey := fmt.Sprintf("%s/%s", ownerExistenceKeyPrefix, owner) + + if exist, ok := client.responseCache.Get(cacheKey); ok && exist != nil { + return exist.(bool), nil + } + + _, resp, err := client.sdkClient.GetUserInfo(owner) + if resp.StatusCode == http.StatusOK && err == nil { + if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return true, nil + } else if resp.StatusCode != http.StatusNotFound { + return false, err + } + + _, resp, err = client.sdkClient.GetOrg(owner) + if resp.StatusCode == http.StatusOK && err == nil { + if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return true, nil + } else if resp.StatusCode != http.StatusNotFound { + return false, err + } + if err := client.responseCache.Set(cacheKey, false, ownerExistenceCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + return false, nil +} + func (client *Client) getMimeTypeByExtension(resource string) string { mimeType := mime.TypeByExtension(path.Ext(resource)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) diff --git a/server/startup.go b/server/startup.go index ffdabb7..149a07d 100644 --- a/server/startup.go +++ b/server/startup.go @@ -110,6 +110,8 @@ func Serve(ctx *cli.Context) error { cfg.Server.PagesBranches[0], keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, certDB, + cfg.ACME.NoDNS01, + cfg.Server.RawDomain, )) interval := 12 * time.Hour From 9ffdc9d4f9406b0cb2e1d07346f65c557a58568a Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Thu, 18 Apr 2024 21:03:16 +0000 Subject: [PATCH 224/320] Refactor redirect code and add tests (#304) Move repetitive code from Options.matchRedirects into a new Redirect.rewriteURL method and add a new test file. No functional changes are intended; this is in preparation for a later change to address #269. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/304 Reviewed-by: crapStone Co-authored-by: Daniel Erat Co-committed-by: Daniel Erat --- server/upstream/redirects.go | 75 +++++++++++++------------------ server/upstream/redirects_test.go | 37 +++++++++++++++ 2 files changed, 68 insertions(+), 44 deletions(-) create mode 100644 server/upstream/redirects_test.go diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index dd36a84..e0601d8 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -17,6 +17,24 @@ type Redirect struct { StatusCode int } +// rewriteURL returns the destination URL and true if r matches reqURL. +func (r *Redirect) rewriteURL(reqURL string) (dstURL string, ok bool) { + // check if from url matches request url + if strings.TrimSuffix(r.From, "/") == strings.TrimSuffix(reqURL, "/") { + return r.To, true + } + // handle wildcard redirects + trimmedFromURL := strings.TrimSuffix(r.From, "/*") + if strings.HasSuffix(r.From, "/*") && strings.HasPrefix(reqURL, trimmedFromURL) { + if strings.Contains(r.To, ":splat") { + splatURL := strings.ReplaceAll(r.To, ":splat", strings.TrimPrefix(reqURL, trimmedFromURL)) + return splatURL, true + } + return r.To, true + } + return "", false +} + // redirectsCacheTimeout specifies the timeout for the redirects cache. var redirectsCacheTimeout = 10 * time.Minute @@ -64,52 +82,21 @@ func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.I } func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client, redirects []Redirect, redirectsCache cache.ICache) (final bool) { - if len(redirects) > 0 { - for _, redirect := range redirects { - reqUrl := ctx.Req.RequestURI - // remove repo and branch from request url - reqUrl = strings.TrimPrefix(reqUrl, "/"+o.TargetRepo) - reqUrl = strings.TrimPrefix(reqUrl, "/@"+o.TargetBranch) + reqURL := ctx.Req.RequestURI + // remove repo and branch from request url + reqURL = strings.TrimPrefix(reqURL, "/"+o.TargetRepo) + reqURL = strings.TrimPrefix(reqURL, "/@"+o.TargetBranch) - // check if from url matches request url - if strings.TrimSuffix(redirect.From, "/") == strings.TrimSuffix(reqUrl, "/") { - // do rewrite if status code is 200 - if redirect.StatusCode == 200 { - o.TargetPath = redirect.To - o.Upstream(ctx, giteaClient, redirectsCache) - return true - } else { - ctx.Redirect(redirect.To, redirect.StatusCode) - return true - } - } - - // handle wildcard redirects - trimmedFromUrl := strings.TrimSuffix(redirect.From, "/*") - if strings.HasSuffix(redirect.From, "/*") && strings.HasPrefix(reqUrl, trimmedFromUrl) { - if strings.Contains(redirect.To, ":splat") { - splatUrl := strings.ReplaceAll(redirect.To, ":splat", strings.TrimPrefix(reqUrl, trimmedFromUrl)) - // do rewrite if status code is 200 - if redirect.StatusCode == 200 { - o.TargetPath = splatUrl - o.Upstream(ctx, giteaClient, redirectsCache) - return true - } else { - ctx.Redirect(splatUrl, redirect.StatusCode) - return true - } - } else { - // do rewrite if status code is 200 - if redirect.StatusCode == 200 { - o.TargetPath = redirect.To - o.Upstream(ctx, giteaClient, redirectsCache) - return true - } else { - ctx.Redirect(redirect.To, redirect.StatusCode) - return true - } - } + for _, redirect := range redirects { + if dstURL, ok := redirect.rewriteURL(reqURL); ok { + // do rewrite if status code is 200 + if redirect.StatusCode == 200 { + o.TargetPath = dstURL + o.Upstream(ctx, giteaClient, redirectsCache) + } else { + ctx.Redirect(dstURL, redirect.StatusCode) } + return true } } diff --git a/server/upstream/redirects_test.go b/server/upstream/redirects_test.go new file mode 100644 index 0000000..c1088a9 --- /dev/null +++ b/server/upstream/redirects_test.go @@ -0,0 +1,37 @@ +package upstream + +import ( + "testing" +) + +func TestRedirect_rewriteURL(t *testing.T) { + for _, tc := range []struct { + redirect Redirect + reqURL string + wantDstURL string + wantOk bool + }{ + {Redirect{"/", "/dst", 200}, "/", "/dst", true}, + {Redirect{"/", "/dst", 200}, "/foo", "", false}, + {Redirect{"/src", "/dst", 200}, "/src", "/dst", true}, + {Redirect{"/src", "/dst", 200}, "/foo", "", false}, + {Redirect{"/src", "/dst", 200}, "/src/foo", "", false}, + {Redirect{"/*", "/dst", 200}, "/", "/dst", true}, + {Redirect{"/*", "/dst", 200}, "/src", "/dst", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src", "/dst/", true}, + // TODO: There shouldn't be double-slashes in these URLs: + // https://codeberg.org/Codeberg/pages-server/issues/269 + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/", "/dst//", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo", "/dst//foo", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo/bar", "/dst//foo/bar", true}, + // TODO: This is a workaround for the above bug. Should it be preserved? + {Redirect{"/src/*", "/dst:splat", 200}, "/src/foo", "/dst/foo", true}, + // TODO: This behavior is incorrect; no redirect should be performed. + {Redirect{"/src/*", "/dst", 200}, "/srcfoo", "/dst", true}, + } { + if dstURL, ok := tc.redirect.rewriteURL(tc.reqURL); dstURL != tc.wantDstURL || ok != tc.wantOk { + t.Errorf("%#v.rewriteURL(%q) = %q, %v; want %q, %v", + tc.redirect, tc.reqURL, dstURL, ok, tc.wantDstURL, tc.wantOk) + } + } +} From a986a52755aba425b9a69c94ed48ceb01c56776a Mon Sep 17 00:00:00 2001 From: Moritz Marquardt Date: Thu, 18 Apr 2024 21:19:45 +0000 Subject: [PATCH 225/320] Fix masked error message from Gitea (#306) This would yield to the error "forge client failed" instead of e.g. "404 Not Found". The issue was introduced in cbb2ce6d0732bcf2372cbc201fc1c1f2733aadba. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/306 Reviewed-by: crapStone Co-authored-by: Moritz Marquardt Co-committed-by: Moritz Marquardt --- server/handler/try.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/handler/try.go b/server/handler/try.go index 145b1a9..e76891d 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -1,6 +1,7 @@ package handler import ( + "fmt" "net/http" "strings" @@ -41,7 +42,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, // Try to request the file from the Gitea API if !options.Upstream(ctx, giteaClient, redirectsCache) { - html.ReturnErrorPage(ctx, "forge client failed", ctx.StatusCode) + html.ReturnErrorPage(ctx, fmt.Sprintf("Forge returned %d %s", ctx.StatusCode, http.StatusText(ctx.StatusCode)), ctx.StatusCode) } } From 69fb22a9e793e92e23e74900950ac9f60a049f9d Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Sat, 20 Apr 2024 11:00:15 +0000 Subject: [PATCH 226/320] Avoid extra slashes in redirects with :splat (#308) Remove leading slashes from captured portions of paths when redirecting using splats. This makes a directive like "/articles/* /posts/:splat 302" behave as described in FEATURES.md, i.e. "/articles/foo" now redirects to "/posts/foo" rather than to "/posts//foo". Fixes #269. This also changes the behavior of a redirect like "/articles/* /posts:splat 302". "/articles/foo" will now redirect to "/postsfoo" rather than to "/posts/foo". This change also fixes an issue where paths like "/articles123" would be incorrectly matched by the above patterns. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/308 Reviewed-by: crapStone Co-authored-by: Daniel Erat Co-committed-by: Daniel Erat --- server/upstream/redirects.go | 15 +++++++++------ server/upstream/redirects_test.go | 17 ++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index e0601d8..ddde375 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -24,13 +24,16 @@ func (r *Redirect) rewriteURL(reqURL string) (dstURL string, ok bool) { return r.To, true } // handle wildcard redirects - trimmedFromURL := strings.TrimSuffix(r.From, "/*") - if strings.HasSuffix(r.From, "/*") && strings.HasPrefix(reqURL, trimmedFromURL) { - if strings.Contains(r.To, ":splat") { - splatURL := strings.ReplaceAll(r.To, ":splat", strings.TrimPrefix(reqURL, trimmedFromURL)) - return splatURL, true + if strings.HasSuffix(r.From, "/*") { + trimmedFromURL := strings.TrimSuffix(r.From, "/*") + if reqURL == trimmedFromURL || strings.HasPrefix(reqURL, trimmedFromURL+"/") { + if strings.Contains(r.To, ":splat") { + matched := strings.TrimPrefix(reqURL, trimmedFromURL) + matched = strings.TrimPrefix(matched, "/") + return strings.ReplaceAll(r.To, ":splat", matched), true + } + return r.To, true } - return r.To, true } return "", false } diff --git a/server/upstream/redirects_test.go b/server/upstream/redirects_test.go index c1088a9..6118a70 100644 --- a/server/upstream/redirects_test.go +++ b/server/upstream/redirects_test.go @@ -19,15 +19,14 @@ func TestRedirect_rewriteURL(t *testing.T) { {Redirect{"/*", "/dst", 200}, "/", "/dst", true}, {Redirect{"/*", "/dst", 200}, "/src", "/dst", true}, {Redirect{"/src/*", "/dst/:splat", 200}, "/src", "/dst/", true}, - // TODO: There shouldn't be double-slashes in these URLs: - // https://codeberg.org/Codeberg/pages-server/issues/269 - {Redirect{"/src/*", "/dst/:splat", 200}, "/src/", "/dst//", true}, - {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo", "/dst//foo", true}, - {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo/bar", "/dst//foo/bar", true}, - // TODO: This is a workaround for the above bug. Should it be preserved? - {Redirect{"/src/*", "/dst:splat", 200}, "/src/foo", "/dst/foo", true}, - // TODO: This behavior is incorrect; no redirect should be performed. - {Redirect{"/src/*", "/dst", 200}, "/srcfoo", "/dst", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/", "/dst/", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo", "/dst/foo", true}, + {Redirect{"/src/*", "/dst/:splat", 200}, "/src/foo/bar", "/dst/foo/bar", true}, + {Redirect{"/src/*", "/dst/:splatsuffix", 200}, "/src/foo", "/dst/foosuffix", true}, + {Redirect{"/src/*", "/dst:splat", 200}, "/src/foo", "/dstfoo", true}, + {Redirect{"/src/*", "/dst", 200}, "/srcfoo", "", false}, + // This is the example from FEATURES.md: + {Redirect{"/articles/*", "/posts/:splat", 302}, "/articles/2022/10/12/post-1/", "/posts/2022/10/12/post-1/", true}, } { if dstURL, ok := tc.redirect.rewriteURL(tc.reqURL); dstURL != tc.wantDstURL || ok != tc.wantOk { t.Errorf("%#v.rewriteURL(%q) = %q, %v; want %q, %v", From 019e85a0d0c8315248732e7e04eeab26bb3308cc Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sat, 27 Apr 2024 18:47:42 +0000 Subject: [PATCH 227/320] chore: Configure Renovate (#284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin. 🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged. --- ### Detected Package Files * `Dockerfile` (dockerfile) * `go.mod` (gomod) * `.woodpecker.yml` (woodpecker) ### Configuration Summary Based on the default config's presets, Renovate will: - Start dependency updates only once this onboarding PR is merged - Enable Renovate Dependency Dashboard creation. - Use semantic commit type `fix` for dependencies and `chore` for all others if semantic commits are in use. - Ignore `node_modules`, `bower_components`, `vendor` and various test/tests directories. - Group known monorepo packages together. - Use curated list of recommended non-monorepo package groupings. - Apply crowd-sourced package replacement rules. - Apply crowd-sourced workarounds for known problems with packages. - Run lock file maintenance (updates) early Monday mornings. - Schedule automerge daily. - Schedule for weekends. - Enable Renovate Dependency Dashboard creation. - Use semantic commit type `fix` for dependencies and `chore` for all others if semantic commits are in use. - Ignore `node_modules`, `bower_components`, `vendor` and various test/tests directories. - Group known monorepo packages together. - Use curated list of recommended non-monorepo package groupings. - Apply crowd-sourced package replacement rules. - Apply crowd-sourced workarounds for known problems with packages. - Run lock file maintenance (updates) early Monday mornings. - Enable the pre-commit manager. - Schedule automerge daily. - Schedule for weekends. - Run Renovate on following schedule: every weekend 🔡 Do you want to change how Renovate upgrades your dependencies? Add your custom config to `renovate.json` in this branch. Renovate will update the Pull Request description the next time it runs. --- ### What to Expect With your current configuration, Renovate will create 3 Pull Requests:
fix(deps): update golang.org/x/exp digest to fe59bbe - Schedule: ["every weekend"] - Branch name: `renovate/golang.org-x-exp-digest` - Merge into: `main` - Upgrade golang.org/x/exp to `fe59bbe5cc7f158318a9631d96683d2df264a3c1`
chore(deps): update golang docker tag to v1.22 - Schedule: ["every weekend"] - Branch name: `renovate/golang-1.x` - Merge into: `main` - Upgrade golang to `1.22`
fix(deps): update golang deps non-major - Schedule: ["before 4am"] - Branch name: `renovate/golang-deps-non-major` - Merge into: `main` - Upgrade [github.com/go-acme/lego/v4](https://github.com/go-acme/lego) to `v4.16.1` - Upgrade [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) to `v1.8.1` - Upgrade [github.com/joho/godotenv](https://github.com/joho/godotenv) to `v1.5.1` - Upgrade [github.com/lib/pq](https://github.com/lib/pq) to `v1.10.9` - Upgrade [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) to `v1.14.22` - Upgrade [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) to `v2.2.1` - Upgrade [github.com/reugn/equalizer](https://github.com/reugn/equalizer) to `11d4adaf94377c6ab5d3fc2d434cd539de78cb73` - Upgrade [github.com/rs/zerolog](https://github.com/rs/zerolog) to `v1.32.0` - Upgrade [github.com/stretchr/testify](https://github.com/stretchr/testify) to `v1.9.0` - Upgrade [github.com/urfave/cli/v2](https://github.com/urfave/cli) to `v2.27.1` - Upgrade [go](https://github.com/golang/go) to `1.22.2` - Upgrade xorm.io/xorm to `v1.3.9`

🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't swamp any CI resources or overwhelm the project. See docs for `prhourlylimit` for details. --- > > ⚠ **Warning** > > Please correct - or verify that you can safely ignore - these dependency lookup failures before you merge this PR. > > - `Could not determine new digest for update (go package code.gitea.io/sdk/gitea)` > > Files affected: `go.mod` --- ❓ Got questions? Check out Renovate's [Docs](https://docs.renovatebot.com/), particularly the Getting Started section. If you need any further assistance then you can also [request help here](https://github.com/renovatebot/renovate/discussions). --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Co-authored-by: pat-s Co-authored-by: Patrick Schratz Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/284 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- renovate.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..9dd1cd7 --- /dev/null +++ b/renovate.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":maintainLockFilesWeekly", + ":enablePreCommit", + "schedule:automergeDaily", + "schedule:weekends" + ], + "automergeType": "branch", + "automergeMajor": false, + "automerge": true, + "prConcurrentLimit": 5, + "labels": ["dependencies"], + "packageRules": [ + { + "matchManagers": ["gomod", "dockerfile"] + }, + { + "groupName": "golang deps non-major", + "matchManagers": ["gomod"], + "matchUpdateTypes": ["minor", "patch"], + "extends": ["schedule:daily"] + } + ], + "postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths"] +} From 7092883ebe08e4dcace543518b0a5a783bed5d6c Mon Sep 17 00:00:00 2001 From: pat-s Date: Sat, 27 Apr 2024 19:14:01 +0000 Subject: [PATCH 228/320] Address CI warnings (#312) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/312 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- .woodpecker.yml | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index de8341c..370ffed 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,5 +1,6 @@ when: branch: main + event: ["push", "pull_request"] steps: # use vendor to cache dependencies @@ -10,7 +11,7 @@ steps: lint: image: golangci/golangci-lint:latest - group: compliant + depends_on: vendor pull: true commands: - go version @@ -19,46 +20,46 @@ steps: - golangci-lint run --timeout 5m --build-tags integration editor-config: - group: compliant + depends_on: [] image: mstruebing/editorconfig-checker build: - group: compliant + depends_on: vendor image: codeberg.org/6543/docker-images/golang_just commands: - go version - just build when: - event: [ "pull_request", "push" ] + event: ["pull_request", "push"] docker-dryrun: - group: compliant + depends_on: vendor image: plugins/kaniko settings: dockerfile: Dockerfile no_push: true tags: latest when: - event: [ "pull_request", "push" ] + event: ["pull_request", "push"] path: Dockerfile build-tag: - group: compliant + depends_on: vendor image: codeberg.org/6543/docker-images/golang_just commands: - go version - just build-tag ${CI_COMMIT_TAG##v} when: - event: [ "tag" ] + event: ["tag"] test: - group: test + depends_on: build image: codeberg.org/6543/docker-images/golang_just commands: - just test integration-tests: - group: test + depends_on: build image: codeberg.org/6543/docker-images/golang_just commands: - just integration @@ -69,6 +70,7 @@ steps: - PORT=4430 release: + depends_on: build image: plugins/gitea-release settings: base_url: https://codeberg.org @@ -77,14 +79,15 @@ steps: api_key: from_secret: bot_token environment: - - DRONE_REPO_OWNER=${CI_REPO_OWNER} - - DRONE_REPO_NAME=${CI_REPO_NAME} - - DRONE_BUILD_EVENT=${CI_BUILD_EVENT} - - DRONE_COMMIT_REF=${CI_COMMIT_REF} + - CI_REPO_OWNER=${CI_REPO_OWNER} + - CI_REPO_NAME=${CI_REPO_NAME} + - CI_BUILD_EVENT=${CI_BUILD_EVENT} + - CI_COMMIT_REF=${CI_COMMIT_REF} when: - event: [ "tag" ] + event: ["tag"] docker-next: + depends_on: vendor image: plugins/kaniko settings: registry: codeberg.org @@ -96,19 +99,20 @@ steps: password: from_secret: bot_token when: - event: [ "push" ] + event: ["push"] branch: ${CI_REPO_DEFAULT_BRANCH} docker-tag: + depends_on: vendor image: plugins/kaniko settings: registry: codeberg.org dockerfile: Dockerfile repo: codeberg.org/codeberg/pages-server - tags: [ latest, "${CI_COMMIT_TAG}" ] + tags: [latest, "${CI_COMMIT_TAG}"] username: from_secret: bot_user password: from_secret: bot_token when: - event: [ "tag" ] + event: ["tag"] From c14c5474b6d50fdb7b6937a2c9ca3cc112145d11 Mon Sep 17 00:00:00 2001 From: pat-s Date: Sat, 27 Apr 2024 19:40:14 +0000 Subject: [PATCH 229/320] Update gitea sdk and tidy (#314) Manually as renovate has issues looking up the currently pinned digest. It should work afterwards as the gitea sdk (without a digest) is also used in other projects where renovate is doing work. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/314 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- go.mod | 10 +++++----- go.sum | 44 ++++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 47e1e71..e4d880d 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,9 @@ go 1.21 toolchain go1.21.4 require ( - code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f + code.gitea.io/sdk/gitea v0.17.1 github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a + github.com/creasty/defaults v1.7.0 github.com/go-acme/lego/v4 v4.5.3 github.com/go-sql-driver/mysql v1.6.0 github.com/joho/godotenv v1.4.0 @@ -45,7 +46,6 @@ require ( github.com/cloudflare/cloudflare-go v0.20.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/creasty/defaults v1.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/deepmap/oapi-codegen v1.6.1 // indirect @@ -122,11 +122,11 @@ require ( github.com/vultr/govultr/v2 v2.7.1 // indirect go.opencensus.io v0.22.3 // indirect go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect - golang.org/x/crypto v0.14.0 // indirect + golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect google.golang.org/appengine v1.6.5 // indirect diff --git a/go.sum b/go.sum index 1a10599..0687af6 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f h1:nMmwDgUIAWj9XQjzHz5unC3ZMfhhwHRk6rnuwLzdu1o= -code.gitea.io/sdk/gitea v0.16.1-0.20231115014337-e23e8aa3004f/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg= +code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= +code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= @@ -318,7 +318,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= @@ -724,6 +723,7 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -771,9 +771,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -806,6 +806,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -843,7 +844,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -859,8 +862,10 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -917,12 +922,19 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -931,8 +943,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -983,6 +998,7 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 1a332c1d541ef51116423870d896aa00e89f0678 Mon Sep 17 00:00:00 2001 From: pat-s Date: Sat, 27 Apr 2024 22:55:32 +0200 Subject: [PATCH 230/320] fix CI triggers --- .woodpecker.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 370ffed..25beb00 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,6 +1,9 @@ when: - branch: main - event: ["push", "pull_request"] + - event: [pull_request, tag, cron] + - event: push + branch: + - ${CI_REPO_DEFAULT_BRANCH} + - renovate/* steps: # use vendor to cache dependencies From e45a354eef079ebb6fee27c41c690ab66c7da4d2 Mon Sep 17 00:00:00 2001 From: pat-s Date: Sat, 27 Apr 2024 21:26:21 +0000 Subject: [PATCH 231/320] CI modifications (#318) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/318 Co-authored-by: pat-s Co-committed-by: pat-s --- .woodpecker.yml => .woodpecker/build.yml | 60 ++++++++++++++---------- .woodpecker/lint.yml | 26 ++++++++++ 2 files changed, 60 insertions(+), 26 deletions(-) rename .woodpecker.yml => .woodpecker/build.yml (70%) create mode 100644 .woodpecker/lint.yml diff --git a/.woodpecker.yml b/.woodpecker/build.yml similarity index 70% rename from .woodpecker.yml rename to .woodpecker/build.yml index 25beb00..00097b2 100644 --- a/.woodpecker.yml +++ b/.woodpecker/build.yml @@ -5,6 +5,9 @@ when: - ${CI_REPO_DEFAULT_BRANCH} - renovate/* +depends_on: + - lint + steps: # use vendor to cache dependencies vendor: @@ -12,20 +15,6 @@ steps: commands: - go mod vendor - lint: - image: golangci/golangci-lint:latest - depends_on: vendor - pull: true - commands: - - go version - - go install mvdan.cc/gofumpt@latest - - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - - golangci-lint run --timeout 5m --build-tags integration - - editor-config: - depends_on: [] - image: mstruebing/editorconfig-checker - build: depends_on: vendor image: codeberg.org/6543/docker-images/golang_just @@ -33,18 +22,24 @@ steps: - go version - just build when: - event: ["pull_request", "push"] + - event: [push, pull_request] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + - renovate/* docker-dryrun: depends_on: vendor - image: plugins/kaniko + image: plugins/kaniko:1.8.8 settings: dockerfile: Dockerfile no_push: true tags: latest when: - event: ["pull_request", "push"] - path: Dockerfile + - event: [push, pull_request] + branch: + - ${CI_REPO_DEFAULT_BRANCH} + - renovate/* + path: Dockerfile build-tag: depends_on: vendor @@ -53,13 +48,19 @@ steps: - go version - just build-tag ${CI_COMMIT_TAG##v} when: - event: ["tag"] + - event: ["tag"] + branch: + - ${CI_REPO_DEFAULT_BRANCH} test: depends_on: build image: codeberg.org/6543/docker-images/golang_just commands: - just test + when: + - event: pull_request + - event: push + branch: renovate/* integration-tests: depends_on: build @@ -71,10 +72,14 @@ steps: - PAGES_DOMAIN=localhost.mock.directory - RAW_DOMAIN=raw.localhost.mock.directory - PORT=4430 + when: + - event: pull_request + - event: push + branch: renovate/* release: depends_on: build - image: plugins/gitea-release + image: plugins/gitea-release:0.3.1 settings: base_url: https://codeberg.org file_exists: overwrite @@ -87,11 +92,13 @@ steps: - CI_BUILD_EVENT=${CI_BUILD_EVENT} - CI_COMMIT_REF=${CI_COMMIT_REF} when: - event: ["tag"] + - event: ["tag"] + branch: + - ${CI_REPO_DEFAULT_BRANCH} docker-next: depends_on: vendor - image: plugins/kaniko + image: plugins/kaniko:1.8.8 settings: registry: codeberg.org dockerfile: Dockerfile @@ -102,12 +109,12 @@ steps: password: from_secret: bot_token when: - event: ["push"] - branch: ${CI_REPO_DEFAULT_BRANCH} + - event: ["push"] + branch: ${CI_REPO_DEFAULT_BRANCH} docker-tag: depends_on: vendor - image: plugins/kaniko + image: plugins/kaniko:1.8.8 settings: registry: codeberg.org dockerfile: Dockerfile @@ -118,4 +125,5 @@ steps: password: from_secret: bot_token when: - event: ["tag"] + - event: ["push"] + branch: ${CI_REPO_DEFAULT_BRANCH} diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml new file mode 100644 index 0000000..02d9ec8 --- /dev/null +++ b/.woodpecker/lint.yml @@ -0,0 +1,26 @@ +when: + - event: pull_request + - event: push + branch: renovate/* + +steps: + lint: + depends_on: [] + image: golangci/golangci-lint:v1.57.2 + commands: + - go version + - go install mvdan.cc/gofumpt@latest + - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" + - golangci-lint run --timeout 5m --build-tags integration + when: + - event: pull_request + - event: push + branch: renovate/* + + editor-config: + depends_on: [] + image: mstruebing/editorconfig-checker:2.7.2 + when: + - event: pull_request + - event: push + branch: renovate/* From 418afb7357097f22aa64c39dadf80d489236df38 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 27 Apr 2024 21:40:40 +0000 Subject: [PATCH 232/320] add nix flake for dev env (#320) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/320 Co-authored-by: crapStone Co-committed-by: crapStone --- flake.lock | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 26 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b14ba48 --- /dev/null +++ b/flake.lock @@ -0,0 +1,73 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1714030708, + "narHash": "sha256-JOGPOxa8N6ySzB7SQBsh0OVz+UXZriyahgvfNHMIY0Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b0d52b31f7f4d80f8bf38f0253652125579c35ff", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "systems": "systems_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "id": "systems", + "type": "indirect" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..f981ed1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + outputs = { + self, + nixpkgs, + flake-utils, + systems, + }: + flake-utils.lib.eachSystem (import systems) + (system: let + pkgs = import nixpkgs { + inherit system; + }; + in { + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + gcc + go + gofumpt + gopls + gotools + go-tools + sqlite-interactive + ]; + }; + }); +} From 89800d4f3660ea79bfc70ff9f179b08d7cae4f4e Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 28 Apr 2024 08:24:39 +0000 Subject: [PATCH 233/320] chore(deps): update plugins/gitea-release docker tag to v1 (#322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [plugins/gitea-release](https://github.com/drone-plugins/drone-gitea-release) | major | `0.3.1` -> `1.1.0` | --- ### Release Notes
drone-plugins/drone-gitea-release (plugins/gitea-release) ### [`v1.1.0`](https://github.com/drone-plugins/drone-gitea-release/compare/v1.0.0...v1.1.0) [Compare Source](https://github.com/drone-plugins/drone-gitea-release/compare/v1.0.0...v1.1.0)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/322 Reviewed-by: Patrick Schratz Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 00097b2..81821ca 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -79,7 +79,7 @@ steps: release: depends_on: build - image: plugins/gitea-release:0.3.1 + image: plugins/gitea-release:1.1.0 settings: base_url: https://codeberg.org file_exists: overwrite From f407fd3ae40cdb538d3014d1dc25dedb540c6c5c Mon Sep 17 00:00:00 2001 From: pat-s Date: Sun, 28 Apr 2024 20:47:04 +0000 Subject: [PATCH 234/320] Add prettier config and format files (#319) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/319 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- .prettierrc.json | 8 +++++++ .woodpecker/build.yml | 10 ++++----- .woodpecker/lint.yml | 6 +++++ FEATURES.md | 14 ++++++------ README.md | 29 +++++++++++++------------ examples/haproxy-sni/README.md | 4 +++- examples/haproxy-sni/docker-compose.yml | 21 +++++++++--------- html/templates/error.html | 28 +++++------------------- 8 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 .prettierrc.json diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..aed6467 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 2, + "endOfLine": "lf" +} diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 81821ca..a5ff820 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -48,7 +48,7 @@ steps: - go version - just build-tag ${CI_COMMIT_TAG##v} when: - - event: ["tag"] + - event: ['tag'] branch: - ${CI_REPO_DEFAULT_BRANCH} @@ -92,7 +92,7 @@ steps: - CI_BUILD_EVENT=${CI_BUILD_EVENT} - CI_COMMIT_REF=${CI_COMMIT_REF} when: - - event: ["tag"] + - event: ['tag'] branch: - ${CI_REPO_DEFAULT_BRANCH} @@ -109,7 +109,7 @@ steps: password: from_secret: bot_token when: - - event: ["push"] + - event: ['push'] branch: ${CI_REPO_DEFAULT_BRANCH} docker-tag: @@ -119,11 +119,11 @@ steps: registry: codeberg.org dockerfile: Dockerfile repo: codeberg.org/codeberg/pages-server - tags: [latest, "${CI_COMMIT_TAG}"] + tags: [latest, '${CI_COMMIT_TAG}'] username: from_secret: bot_user password: from_secret: bot_token when: - - event: ["push"] + - event: ['push'] branch: ${CI_REPO_DEFAULT_BRANCH} diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 02d9ec8..3b269f6 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -24,3 +24,9 @@ steps: - event: pull_request - event: push branch: renovate/* + + prettier: + image: docker.io/woodpeckerci/plugin-prettier:0.1.0 + depends_on: [] + settings: + version: 3.2.5 diff --git a/FEATURES.md b/FEATURES.md index 3d2f394..52b90e5 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -19,16 +19,16 @@ Redirects can be created with a `_redirects` file with the following format: from to [status] ``` -* Lines starting with `#` are ignored -* `from` - the path to redirect from (Note: repository and branch names are removed from request URLs) -* `to` - the path or URL to redirect to -* `status` - status code to use when redirecting (default 301) +- Lines starting with `#` are ignored +- `from` - the path to redirect from (Note: repository and branch names are removed from request URLs) +- `to` - the path or URL to redirect to +- `status` - status code to use when redirecting (default 301) ### Status codes -* `200` - returns content from specified path (no external URLs) without changing the URL (rewrite) -* `301` - Moved Permanently (Permanent redirect) -* `302` - Found (Temporary redirect) +- `200` - returns content from specified path (no external URLs) without changing the URL (rewrite) +- `301` - Moved Permanently (Permanent redirect) +- `302` - Found (Temporary redirect) ### Examples diff --git a/README.md b/README.md index f47a196..c23d86a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/) [![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server) - + Gitea lacks the ability to host static pages from Git. @@ -21,19 +21,19 @@ and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/). This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. Mapping custom domains is not static anymore, but can be done with DNS: -1) add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The -first line will be the canonical domain/URL; all other occurrences will be redirected to it. +1. add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The + first line will be the canonical domain/URL; all other occurrences will be redirected to it. -2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to -"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else. -If the branch name contains slash characters, you need to replace "/" in the branch name to "~"): - `www.example.org. IN CNAME main.pages.example.codeberg.page.` +2. add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to + "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else. + If the branch name contains slash characters, you need to replace "/" in the branch name to "~"): + `www.example.org. IN CNAME main.pages.example.codeberg.page.` -3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record -for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT -record that points to your repo (just like the CNAME record): - `example.org IN ALIAS codeberg.page.` - `example.org IN TXT main.pages.example.codeberg.page.` +3. if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record + for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT + record that points to your repo (just like the CNAME record): + `example.org IN ALIAS codeberg.page.` + `example.org IN TXT main.pages.example.codeberg.page.` Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. @@ -43,7 +43,8 @@ Certificates are generated, updated and cleaned up automatically via Let's Encry ## Deployment -**Warning: Some Caveats Apply** +**Warning: Some Caveats Apply** + > Currently, the deployment requires you to have some knowledge of system administration as well as understanding and building code, > so you can eventually edit non-configurable and codeberg-specific settings. > In the future, we'll try to reduce these and make hosting Codeberg Pages as easy as setting up Gitea. @@ -74,7 +75,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. -- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. +- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. diff --git a/examples/haproxy-sni/README.md b/examples/haproxy-sni/README.md index 8bb8f02..8165d6c 100644 --- a/examples/haproxy-sni/README.md +++ b/examples/haproxy-sni/README.md @@ -1,8 +1,9 @@ # HAProxy with SNI & Host-based rules -This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), *as well as* to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection). +This is a proof of concept, enabling HAProxy to use _either_ SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), _as well as_ to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection). ## How it works + 1. The `http_redirect_frontend` is only there to listen on port 80 and redirect every request to HTTPS. 2. The `https_sni_frontend` listens on port 443 and chooses a backend based on the SNI hostname of the TLS connection. 3. The `https_termination_backend` passes all requests to a unix socket (using the plain TCP data). @@ -11,6 +12,7 @@ This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listening via HTTPS and is providing its own HTTPS certificates, while the `gitea_backend` only provides HTTP. ## How to test + ```bash docker-compose up & ./test.sh diff --git a/examples/haproxy-sni/docker-compose.yml b/examples/haproxy-sni/docker-compose.yml index 66ff52b..2cac2ad 100644 --- a/examples/haproxy-sni/docker-compose.yml +++ b/examples/haproxy-sni/docker-compose.yml @@ -1,22 +1,21 @@ -version: "3" +version: '3' services: haproxy: image: haproxy - ports: ["443:443"] + ports: ['443:443'] volumes: - - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro - - ./dhparam.pem:/etc/ssl/dhparam.pem:ro - - ./haproxy-certificates:/etc/ssl/private/haproxy:ro + - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + - ./dhparam.pem:/etc/ssl/dhparam.pem:ro + - ./haproxy-certificates:/etc/ssl/private/haproxy:ro cap_add: - - NET_ADMIN + - NET_ADMIN gitea: image: caddy volumes: - - ./gitea-www:/srv:ro - - ./gitea.Caddyfile:/etc/caddy/Caddyfile:ro + - ./gitea-www:/srv:ro + - ./gitea.Caddyfile:/etc/caddy/Caddyfile:ro pages: image: caddy volumes: - - ./pages-www:/srv:ro - - ./pages.Caddyfile:/etc/caddy/Caddyfile:ro - + - ./pages-www:/srv:ro + - ./pages.Caddyfile:/etc/caddy/Caddyfile:ro diff --git a/html/templates/error.html b/html/templates/error.html index 6094a26..05a5d46 100644 --- a/html/templates/error.html +++ b/html/templates/error.html @@ -1,18 +1,12 @@ - + {{.StatusText}} - - + + - + @@ -50,18 +39,13 @@

"{{.Message}}"

We hope this isn't a problem on our end ;) - Make sure to check the - troubleshooting section in the Docs!

- + Static pages made easy - Codeberg Pages From 8cba7f9c8ad591b2c2ebef8f0978343ab3a14ead Mon Sep 17 00:00:00 2001 From: pat-s Date: Sun, 28 Apr 2024 21:48:07 +0000 Subject: [PATCH 235/320] Add yamllint (#321) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/321 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- .woodpecker/lint.yml | 10 ++++++++++ .yamllint.yaml | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 .yamllint.yaml diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 3b269f6..8a90a15 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -25,6 +25,16 @@ steps: - event: push branch: renovate/* + yamllint: + image: pipelinecomponents/yamllint:0.31.1 + depends_on: [] + commands: + - yamllint . + when: + - event: pull_request + - event: push + branch: renovate/* + prettier: image: docker.io/woodpeckerci/plugin-prettier:0.1.0 depends_on: [] diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..2b1f87c --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,19 @@ +extends: default + +rules: + comments: + require-starting-space: false + ignore-shebangs: true + min-spaces-from-content: 1 + braces: + min-spaces-inside: 1 + max-spaces-inside: 1 + document-start: + present: false + indentation: + spaces: 2 + indent-sequences: true + line-length: + max: 256 + new-lines: + type: unix From d09c6e12180467d5811e5ec929e57f633861cd17 Mon Sep 17 00:00:00 2001 From: pat-s Date: Mon, 29 Apr 2024 12:46:00 +0000 Subject: [PATCH 236/320] Multiarch images (#324) taken from #214 Configured to only build multiarch when also publishing (when running in `main` or for a release) ## Build time - amd64 & arm64: 07:42 - amd64 only: 04:26 Build time via kaniko building only amd64: ~ 6/7 min Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/324 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- .woodpecker/build.yml | 11 +++++++---- Dockerfile | 31 ++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index a5ff820..1be4885 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -29,10 +29,11 @@ steps: docker-dryrun: depends_on: vendor - image: plugins/kaniko:1.8.8 + image: woodpeckerci/plugin-docker-buildx:3.2.1 settings: dockerfile: Dockerfile - no_push: true + platforms: linux/amd64 + dry-run: true tags: latest when: - event: [push, pull_request] @@ -98,10 +99,11 @@ steps: docker-next: depends_on: vendor - image: plugins/kaniko:1.8.8 + image: woodpeckerci/plugin-docker-buildx:3.2.1 settings: registry: codeberg.org dockerfile: Dockerfile + platforms: linux/amd64,arm64 repo: codeberg.org/codeberg/pages-server tags: next username: @@ -114,10 +116,11 @@ steps: docker-tag: depends_on: vendor - image: plugins/kaniko:1.8.8 + image: woodpeckerci/plugin-docker-buildx:3.2.1 settings: registry: codeberg.org dockerfile: Dockerfile + platforms: linux/amd64,arm64 repo: codeberg.org/codeberg/pages-server tags: [latest, '${CI_COMMIT_TAG}'] username: diff --git a/Dockerfile b/Dockerfile index eec97de..9c05cda 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,35 @@ -FROM techknowlogick/xgo as build +# Set the default Go version as a build argument +ARG XGO="go-1.21.x" -WORKDIR /workspace +# Use xgo (a Go cross-compiler tool) as build image +FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} as build -COPY . . -RUN CGO_ENABLED=1 go build -tags 'sqlite sqlite_unlock_notify netgo' -ldflags '-s -w -extldflags "-static" -linkmode external' . +# Set the working directory and copy the source code +WORKDIR /go/src/codeberg.org/codeberg/pages +COPY . /go/src/codeberg.org/codeberg/pages +# Set the target architecture (can be set using --build-arg), buildx set it automatically +ARG TARGETOS TARGETARCH + +# Build the binary using xgo +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=1 \ + xgo -x -v --targets=${TARGETOS}/${TARGETARCH} -tags='sqlite sqlite_unlock_notify netgo' -ldflags='-s -w -extldflags "-static" -linkmode external' -out pages . + +# Use a scratch image as the base image for the final container, +# which will contain only the built binary and the CA certificates FROM scratch -COPY --from=build /workspace/pages /pages + +# Copy the built binary and the CA certificates from the build container to the final container +COPY --from=build /go/src/codeberg.org/codeberg/pages/ /pages COPY --from=build \ /etc/ssl/certs/ca-certificates.crt \ /etc/ssl/certs/ca-certificates.crt +# Expose ports 80 and 443 for the built binary to listen on +EXPOSE 80/tcp +EXPOSE 443/tcp + +# Set the entrypoint for the container to the built binary ENTRYPOINT ["/pages"] From ca9433e0eae1ced3f356c86413c589529be02bd7 Mon Sep 17 00:00:00 2001 From: pat-s Date: Mon, 29 Apr 2024 14:48:00 +0200 Subject: [PATCH 237/320] run lint workflow on main branch to unblock build workflow --- .woodpecker/lint.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 8a90a15..0977645 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -1,7 +1,9 @@ when: - event: pull_request - event: push - branch: renovate/* + branch: + - ${CI_REPO_DEFAULT_BRANCH} + - renovate/** steps: lint: From 56d44609eaa23212c0bf09d5284c9d782aaeccfc Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 30 Apr 2024 19:50:03 +0000 Subject: [PATCH 238/320] Add option to start http server for profiling (#323) https://rafallorenz.com/go/go-profiling-http-service-with-pprof-and-expvar/ Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/323 Co-authored-by: crapStone Co-committed-by: crapStone --- Justfile | 4 ++-- README.md | 15 +++++++++++++++ cli/flags.go | 12 ++++++++++++ server/profiling.go | 21 +++++++++++++++++++++ server/startup.go | 8 ++++---- 5 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 server/profiling.go diff --git a/Justfile b/Justfile index 0bf38a3..231df7f 100644 --- a/Justfile +++ b/Justfile @@ -1,13 +1,13 @@ CGO_FLAGS := '-extldflags "-static" -linkmode external' TAGS := 'sqlite sqlite_unlock_notify netgo' -dev: +dev *FLAGS: #!/usr/bin/env bash set -euxo pipefail set -a # automatically export all variables source .env-dev set +a - go run -tags '{{TAGS}}' . + go run -tags '{{TAGS}}' . {{FLAGS}} build: CGO_ENABLED=1 go build -tags '{{TAGS}}' -ldflags '-s -w {{CGO_FLAGS}}' -v -o build/codeberg-pages-server ./ diff --git a/README.md b/README.md index c23d86a..a1d6424 100644 --- a/README.md +++ b/README.md @@ -124,3 +124,18 @@ now these pages should work: - - - + +### Profiling + +> This section is just a collection of commands for quick reference. If you want to learn more about profiling read [this](https://go.dev/doc/diagnostics) article or google `golang profiling`. + +First enable profiling by supplying the cli arg `--enable-profiling` or using the environment variable `EENABLE_PROFILING`. + +Get cpu and mem stats: + +```bash +go tool pprof -raw -output=cpu.txt 'http://localhost:9999/debug/pprof/profile?seconds=60' & +curl -so mem.txt 'http://localhost:9999/debug/pprof/heap?seconds=60' +``` + +More endpoints are documented here: diff --git a/cli/flags.go b/cli/flags.go index 52e1c1c..f7a7dc8 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -139,6 +139,18 @@ var ( EnvVars: []string{"CONFIG_FILE"}, }, + &cli.BoolFlag{ + Name: "enable-profiling", + Usage: "enables the go http profiling endpoints", + EnvVars: []string{"ENABLE_PROFILING"}, + }, + &cli.StringFlag{ + Name: "profiling-address", + Usage: "specify ip address and port the profiling server should listen on", + EnvVars: []string{"PROFILING_ADDRESS"}, + Value: "localhost:9999", + }, + // ############################ // ### ACME Client Settings ### // ############################ diff --git a/server/profiling.go b/server/profiling.go new file mode 100644 index 0000000..7d20926 --- /dev/null +++ b/server/profiling.go @@ -0,0 +1,21 @@ +package server + +import ( + "net/http" + _ "net/http/pprof" + + "github.com/rs/zerolog/log" +) + +func StartProfilingServer(listeningAddress string) { + server := &http.Server{ + Addr: listeningAddress, + Handler: http.DefaultServeMux, + } + + log.Info().Msgf("Starting debug server on %s", listeningAddress) + + go func() { + log.Fatal().Err(server.ListenAndServe()).Msg("Failed to start debug server") + }() +} diff --git a/server/startup.go b/server/startup.go index 149a07d..fd89803 100644 --- a/server/startup.go +++ b/server/startup.go @@ -3,7 +3,6 @@ package server import ( "context" "crypto/tls" - "encoding/json" "fmt" "net" "net/http" @@ -43,9 +42,6 @@ func Serve(ctx *cli.Context) error { } log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) - foo, _ := json.Marshal(cfg) - log.Trace().RawJSON("config", foo).Msg("starting server with config") - listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort) @@ -133,6 +129,10 @@ func Serve(ctx *cli.Context) error { }() } + if ctx.IsSet("enable-profiling") { + StartProfilingServer(ctx.String("profiling-address")) + } + // Create ssl handler based on settings sslHandler := handler.Handler(cfg.Server, giteaClient, dnsLookupCache, canonicalDomainCache, redirectsCache) From eb08c46dcd85f5b93b4339d4a77ad669762ce1cb Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Thu, 2 May 2024 13:20:17 +0000 Subject: [PATCH 239/320] chore(deps): update golang docker tag to v1.22 (#326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | golang | minor | `1.21` -> `1.22` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/326 Reviewed-by: crapStone Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 1be4885..f2cb615 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -11,7 +11,7 @@ depends_on: steps: # use vendor to cache dependencies vendor: - image: golang:1.21 + image: golang:1.22 commands: - go mod vendor From 7c49c4b967d47f74e88b5223e872ca6da750955c Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 5 May 2024 00:59:58 +0000 Subject: [PATCH 240/320] chore(deps): update golangci/golangci-lint docker tag to v1.58.0 (#330) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 0977645..ee75113 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -8,7 +8,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.57.2 + image: golangci/golangci-lint:v1.58.0 commands: - go version - go install mvdan.cc/gofumpt@latest From adf13bfdbcb7c7f60a1684395984611ab6e15ae0 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 7 May 2024 09:09:27 +0000 Subject: [PATCH 241/320] chore(deps): update mstruebing/editorconfig-checker docker tag to v3 (#329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | mstruebing/editorconfig-checker | major | `2.7.2` -> `v3.0.1` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/329 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index ee75113..cc361c0 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -21,7 +21,7 @@ steps: editor-config: depends_on: [] - image: mstruebing/editorconfig-checker:2.7.2 + image: mstruebing/editorconfig-checker:v3.0.1 when: - event: pull_request - event: push From d74f1fe8a4cedc348e25d15831a05b23cb15c358 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 12 May 2024 00:55:23 +0000 Subject: [PATCH 242/320] chore(deps): update golangci/golangci-lint docker tag to v1.58.1 (#333) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index cc361c0..5efcca1 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -8,7 +8,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.58.0 + image: golangci/golangci-lint:v1.58.1 commands: - go version - go install mvdan.cc/gofumpt@latest From c1df2f068b8fac7afaa98d63056945c5f7497c75 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 19 May 2024 01:11:30 +0000 Subject: [PATCH 243/320] fix(deps): update golang.org/x/exp digest to 9bf2ced (#335) --- go.mod | 2 +- go.sum | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index e4d880d..518dff0 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 0687af6..ae24fc3 100644 --- a/go.sum +++ b/go.sum @@ -254,8 +254,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -784,8 +784,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= -golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -807,8 +807,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -864,8 +865,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -999,8 +1001,9 @@ golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From b54cd38d0be564dad2dce2e25c82dffed7bc4428 Mon Sep 17 00:00:00 2001 From: pat-s Date: Sun, 19 May 2024 16:18:11 +0000 Subject: [PATCH 244/320] Fix Dockerfile binary location (#337) I know get the following ``` docker run --rm pages-server:test 12:40PM ERR A fatal error occurred error="could not create new gitea client: Get \"/api/v1/version\": unsupported protocol scheme \"\"" ``` which I am not sure is OK as doing the same with v5.1 results in ``` docker run --platform linux/amd64 --rm -it codeberg.org/codeberg/pages-server:v5.1 ACME client has wrong config: you must set $ACME_ACCEPT_TERMS and $DNS_PROVIDER, unless $ACME_API is set to https://acme.mock.directory ``` The error is the same though what I get when building of 8cba7f9c8ad591b2c2ebef8f0978343ab3a14ead (just before merging the multi-arch PR). Not sure if this ERR is expected but it should be unrelated to the multiarch approach. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/337 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9c05cda..6106317 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,13 +16,14 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg \ GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=1 \ xgo -x -v --targets=${TARGETOS}/${TARGETARCH} -tags='sqlite sqlite_unlock_notify netgo' -ldflags='-s -w -extldflags "-static" -linkmode external' -out pages . +RUN mv -vf /build/pages-* /go/src/codeberg.org/codeberg/pages/pages # Use a scratch image as the base image for the final container, # which will contain only the built binary and the CA certificates FROM scratch # Copy the built binary and the CA certificates from the build container to the final container -COPY --from=build /go/src/codeberg.org/codeberg/pages/ /pages +COPY --from=build /go/src/codeberg.org/codeberg/pages/pages /pages COPY --from=build \ /etc/ssl/certs/ca-certificates.crt \ /etc/ssl/certs/ca-certificates.crt From 69361c69c143ab3edce230269d8a5084b16fb580 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 20 May 2024 00:39:02 +0000 Subject: [PATCH 245/320] chore(deps): update pipelinecomponents/yamllint docker tag to v0.31.2 (#336) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 5efcca1..0dde00e 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -28,7 +28,7 @@ steps: branch: renovate/* yamllint: - image: pipelinecomponents/yamllint:0.31.1 + image: pipelinecomponents/yamllint:0.31.2 depends_on: [] commands: - yamllint . From 885cfac2ec4f04f5a62401a71f4e598b06445f74 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 26 May 2024 01:26:39 +0000 Subject: [PATCH 246/320] chore(deps): update golangci/golangci-lint docker tag to v1.58.2 (#341) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 0dde00e..cf59edc 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -8,7 +8,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.58.1 + image: golangci/golangci-lint:v1.58.2 commands: - go version - go install mvdan.cc/gofumpt@latest From eea009c7febce63ed21b8184af244181aa266a35 Mon Sep 17 00:00:00 2001 From: pat-s Date: Sun, 26 May 2024 14:34:03 +0000 Subject: [PATCH 247/320] Allow building PR images on demand (#340) Triggered when the label is set in a PR. Helps to test changes in PRs. Co-authored-by: crapStone Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/340 Reviewed-by: crapStone Co-authored-by: pat-s Co-committed-by: pat-s --- .woodpecker/build.yml | 17 +++++++++++++++++ README.md | 11 +++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index f2cb615..a86c7fe 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -114,6 +114,23 @@ steps: - event: ['push'] branch: ${CI_REPO_DEFAULT_BRANCH} + 'Publish PR image': + image: woodpeckerci/plugin-docker-buildx:3.2.1 + depends_on: test + settings: + registry: codeberg.org + dockerfile: Dockerfile + platforms: linux/amd64 + repo: codeberg.org/codeberg/pages-server + tags: next + username: + from_secret: bot_user + password: + from_secret: bot_token + when: + evaluate: 'CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_image"' + event: pull_request + docker-tag: depends_on: vendor image: woodpeckerci/plugin-docker-buildx:3.2.1 diff --git a/README.md b/README.md index a1d6424..34143f1 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,9 @@ but forward the requests on the IP level to the Pages Server. You can check out a proof of concept in the `examples/haproxy-sni` folder, and especially have a look at [this section of the haproxy.cfg](https://codeberg.org/Codeberg/pages-server/src/branch/main/examples/haproxy-sni/haproxy.cfg#L38). +If you want to test a change, you can open a PR and ask for the label `build_pr_image` to be added. +This will trigger a build of the PR which will build a docker image to be used for testing. + ### Environment Variables - `HOST` & `PORT` (default: `[::]` & `443`): listen address. @@ -72,14 +75,14 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg - `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. - `RAW_INFO_PAGE` (default: ): info page for raw resources, shown if no resource is provided. -- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). +- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. -- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. +- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See for available values & additional environment variables. - `NO_DNS_01` (default: `false`): Disable the use of ACME DNS. This means that the wildcard certificate is self-signed and all domains and subdomains will have a distinct certificate. Because this may lead to a rate limit from the ACME provider, this option is not recommended for Gitea/Forgejo instances with open registrations or a great number of users/orgs. - `LOG_LEVEL` (default: warn): Set this to specify the level of logging. @@ -104,7 +107,7 @@ Previous maintainers: ### First steps -The code of this repository is split in several modules. +The code of this repository is split in several modules. The [Architecture is explained](https://codeberg.org/Codeberg/pages-server/wiki/Architecture) in the wiki. The `cmd` folder holds the data necessary for interacting with the service via the cli. @@ -117,7 +120,7 @@ Thank you very much. Make sure you have [golang](https://go.dev) v1.21 or newer and [just](https://just.systems/man/en/) installed. -run `just dev` +run `just dev` now these pages should work: - From 77a8439ea7b59e40b88598589ca4653e4959b24b Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 26 May 2024 14:45:03 +0000 Subject: [PATCH 248/320] Rename gitea to forge in cli args and env variables (#339) This PR renames `gitea` in cli args to `forge` and `GITEA` in environment variables to `FORGE` and adds the gitea names as aliases for the forge names. Also closes #311 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/339 --- cli/flags.go | 24 ++++++----- config/assets/test_config.toml | 4 +- config/config.go | 4 +- config/setup.go | 12 +++--- config/setup_test.go | 73 +++++++++++++++++++++++----------- example_config.toml | 2 +- server/gitea/client.go | 9 +++-- server/handler/handler_test.go | 2 +- server/startup.go | 2 +- 9 files changed, 81 insertions(+), 51 deletions(-) diff --git a/cli/flags.go b/cli/flags.go index f7a7dc8..934ef8d 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -22,29 +22,31 @@ var ( ServerFlags = append(CertStorageFlags, []cli.Flag{ // ############# - // ### Gitea ### + // ### Forge ### // ############# - // GiteaRoot specifies the root URL of the Gitea instance, without a trailing slash. + // ForgeRoot specifies the root URL of the Forge instance, without a trailing slash. &cli.StringFlag{ - Name: "gitea-root", - Usage: "specifies the root URL of the Gitea instance, without a trailing slash.", - EnvVars: []string{"GITEA_ROOT"}, + Name: "forge-root", + Aliases: []string{"gitea-root"}, + Usage: "specifies the root URL of the Forgejo/Gitea instance, without a trailing slash.", + EnvVars: []string{"FORGE_ROOT", "GITEA_ROOT"}, }, - // GiteaApiToken specifies an api token for the Gitea instance + // ForgeApiToken specifies an api token for the Forge instance &cli.StringFlag{ - Name: "gitea-api-token", - Usage: "specifies an api token for the Gitea instance", - EnvVars: []string{"GITEA_API_TOKEN"}, + Name: "forge-api-token", + Aliases: []string{"gitea-api-token"}, + Usage: "specifies an api token for the Forgejo/Gitea instance", + EnvVars: []string{"FORGE_API_TOKEN", "GITEA_API_TOKEN"}, }, &cli.BoolFlag{ Name: "enable-lfs-support", - Usage: "enable lfs support, require gitea >= v1.17.0 as backend", + Usage: "enable lfs support, gitea must be version v1.17.0 or higher", EnvVars: []string{"ENABLE_LFS_SUPPORT"}, Value: false, }, &cli.BoolFlag{ Name: "enable-symlink-support", - Usage: "follow symlinks if enabled, require gitea >= v1.18.0 as backend", + Usage: "follow symlinks if enabled, gitea must be version v1.18.0 or higher", EnvVars: []string{"ENABLE_SYMLINK_SUPPORT"}, Value: false, }, diff --git a/config/assets/test_config.toml b/config/assets/test_config.toml index 6a2f0d0..acb2c55 100644 --- a/config/assets/test_config.toml +++ b/config/assets/test_config.toml @@ -10,8 +10,8 @@ rawDomain = 'raw.codeberg.page' allowedCorsDomains = ['fonts.codeberg.org', 'design.codeberg.org'] blacklistedPaths = ['do/not/use'] -[gitea] -root = 'codeberg.org' +[forge] +root = 'https://codeberg.org' token = 'XXXXXXXX' lfsEnabled = true followSymlinks = true diff --git a/config/config.go b/config/config.go index 0146e0f..2accbf5 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,7 @@ package config type Config struct { LogLevel string `default:"warn"` Server ServerConfig - Gitea GiteaConfig + Forge ForgeConfig Database DatabaseConfig ACME ACMEConfig } @@ -20,7 +20,7 @@ type ServerConfig struct { BlacklistedPaths []string } -type GiteaConfig struct { +type ForgeConfig struct { Root string Token string LFSEnabled bool `default:"false"` diff --git a/config/setup.go b/config/setup.go index 6a2aa62..f1388fe 100644 --- a/config/setup.go +++ b/config/setup.go @@ -51,7 +51,7 @@ func MergeConfig(ctx *cli.Context, config *Config) { } mergeServerConfig(ctx, &config.Server) - mergeGiteaConfig(ctx, &config.Gitea) + mergeForgeConfig(ctx, &config.Forge) mergeDatabaseConfig(ctx, &config.Database) mergeACMEConfig(ctx, &config.ACME) } @@ -89,12 +89,12 @@ func mergeServerConfig(ctx *cli.Context, config *ServerConfig) { config.BlacklistedPaths = append(config.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...) } -func mergeGiteaConfig(ctx *cli.Context, config *GiteaConfig) { - if ctx.IsSet("gitea-root") { - config.Root = ctx.String("gitea-root") +func mergeForgeConfig(ctx *cli.Context, config *ForgeConfig) { + if ctx.IsSet("forge-root") { + config.Root = ctx.String("forge-root") } - if ctx.IsSet("gitea-api-token") { - config.Token = ctx.String("gitea-api-token") + if ctx.IsSet("forge-api-token") { + config.Token = ctx.String("forge-api-token") } if ctx.IsSet("enable-lfs-support") { config.LFSEnabled = ctx.Bool("enable-lfs-support") diff --git a/config/setup_test.go b/config/setup_test.go index 1a32740..6ca9712 100644 --- a/config/setup_test.go +++ b/config/setup_test.go @@ -110,7 +110,7 @@ func TestValuesReadFromConfigFileShouldBeOverwrittenByArgs(t *testing.T) { } expectedConfig.LogLevel = "debug" - expectedConfig.Gitea.Root = "not-codeberg.org" + expectedConfig.Forge.Root = "not-codeberg.org" expectedConfig.ACME.AcceptTerms = true expectedConfig.Server.Host = "172.17.0.2" expectedConfig.Server.BlacklistedPaths = append(expectedConfig.Server.BlacklistedPaths, ALWAYS_BLACKLISTED_PATHS...) @@ -122,7 +122,7 @@ func TestValuesReadFromConfigFileShouldBeOverwrittenByArgs(t *testing.T) { []string{ "--config-file", "assets/test_config.toml", "--log-level", "debug", - "--gitea-root", "not-codeberg.org", + "--forge-root", "not-codeberg.org", "--acme-accept-terms", "--host", "172.17.0.2", }, @@ -146,7 +146,7 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T AllowedCorsDomains: []string{"original"}, BlacklistedPaths: []string{"original"}, }, - Gitea: GiteaConfig{ + Forge: ForgeConfig{ Root: "original", Token: "original", LFSEnabled: false, @@ -186,7 +186,7 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T AllowedCorsDomains: []string{"changed"}, BlacklistedPaths: append([]string{"changed"}, ALWAYS_BLACKLISTED_PATHS...), }, - Gitea: GiteaConfig{ + Forge: ForgeConfig{ Root: "changed", Token: "changed", LFSEnabled: true, @@ -227,9 +227,9 @@ func TestMergeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T "--port", "8443", "--http-port", "443", "--enable-http-server", - // Gitea - "--gitea-root", "changed", - "--gitea-api-token", "changed", + // Forge + "--forge-root", "changed", + "--forge-api-token", "changed", "--enable-lfs-support", "--enable-symlink-support", "--default-mime-type", "changed", @@ -366,11 +366,11 @@ func TestMergeServerConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgE } } -func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { +func TestMergeForgeConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { runApp( t, func(ctx *cli.Context) error { - cfg := &GiteaConfig{ + cfg := &ForgeConfig{ Root: "original", Token: "original", LFSEnabled: false, @@ -379,9 +379,9 @@ func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *test ForbiddenMimeTypes: []string{"original"}, } - mergeGiteaConfig(ctx, cfg) + mergeForgeConfig(ctx, cfg) - expectedConfig := &GiteaConfig{ + expectedConfig := &ForgeConfig{ Root: "changed", Token: "changed", LFSEnabled: true, @@ -395,8 +395,8 @@ func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *test return nil }, []string{ - "--gitea-root", "changed", - "--gitea-api-token", "changed", + "--forge-root", "changed", + "--forge-api-token", "changed", "--enable-lfs-support", "--enable-symlink-support", "--default-mime-type", "changed", @@ -405,25 +405,25 @@ func TestMergeGiteaConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *test ) } -func TestMergeGiteaConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { +func TestMergeForgeConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgExists(t *testing.T) { type testValuePair struct { args []string - callback func(*GiteaConfig) + callback func(*ForgeConfig) } testValuePairs := []testValuePair{ - {args: []string{"--gitea-root", "changed"}, callback: func(gc *GiteaConfig) { gc.Root = "changed" }}, - {args: []string{"--gitea-api-token", "changed"}, callback: func(gc *GiteaConfig) { gc.Token = "changed" }}, - {args: []string{"--enable-lfs-support"}, callback: func(gc *GiteaConfig) { gc.LFSEnabled = true }}, - {args: []string{"--enable-symlink-support"}, callback: func(gc *GiteaConfig) { gc.FollowSymlinks = true }}, - {args: []string{"--default-mime-type", "changed"}, callback: func(gc *GiteaConfig) { gc.DefaultMimeType = "changed" }}, - {args: []string{"--forbidden-mime-types", "changed"}, callback: func(gc *GiteaConfig) { gc.ForbiddenMimeTypes = []string{"changed"} }}, + {args: []string{"--forge-root", "changed"}, callback: func(gc *ForgeConfig) { gc.Root = "changed" }}, + {args: []string{"--forge-api-token", "changed"}, callback: func(gc *ForgeConfig) { gc.Token = "changed" }}, + {args: []string{"--enable-lfs-support"}, callback: func(gc *ForgeConfig) { gc.LFSEnabled = true }}, + {args: []string{"--enable-symlink-support"}, callback: func(gc *ForgeConfig) { gc.FollowSymlinks = true }}, + {args: []string{"--default-mime-type", "changed"}, callback: func(gc *ForgeConfig) { gc.DefaultMimeType = "changed" }}, + {args: []string{"--forbidden-mime-types", "changed"}, callback: func(gc *ForgeConfig) { gc.ForbiddenMimeTypes = []string{"changed"} }}, } for _, pair := range testValuePairs { runApp( t, func(ctx *cli.Context) error { - cfg := GiteaConfig{ + cfg := ForgeConfig{ Root: "original", Token: "original", LFSEnabled: false, @@ -435,7 +435,7 @@ func TestMergeGiteaConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgEx expectedConfig := cfg pair.callback(&expectedConfig) - mergeGiteaConfig(ctx, &cfg) + mergeForgeConfig(ctx, &cfg) expectedConfig.ForbiddenMimeTypes = fixArrayFromCtx(ctx, "forbidden-mime-types", expectedConfig.ForbiddenMimeTypes) @@ -448,6 +448,33 @@ func TestMergeGiteaConfigShouldReplaceOnlyOneValueExistingValueGivenOnlyOneArgEx } } +func TestMergeForgeConfigShouldReplaceValuesGivenGiteaOptionsExist(t *testing.T) { + runApp( + t, + func(ctx *cli.Context) error { + cfg := &ForgeConfig{ + Root: "original", + Token: "original", + } + + mergeForgeConfig(ctx, cfg) + + expectedConfig := &ForgeConfig{ + Root: "changed", + Token: "changed", + } + + assert.Equal(t, expectedConfig, cfg) + + return nil + }, + []string{ + "--gitea-root", "changed", + "--gitea-api-token", "changed", + }, + ) +} + func TestMergeDatabaseConfigShouldReplaceAllExistingValuesGivenAllArgsExist(t *testing.T) { runApp( t, diff --git a/example_config.toml b/example_config.toml index 30e77c4..c8dacb2 100644 --- a/example_config.toml +++ b/example_config.toml @@ -11,7 +11,7 @@ pagesBranches = ["pages"] allowedCorsDomains = [] blacklistedPaths = [] -[gitea] +[forge] root = 'https://codeberg.org' token = 'ASDF1234' lfsEnabled = true diff --git a/server/gitea/client.go b/server/gitea/client.go index 5955bfb..3abb487 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -57,12 +57,13 @@ type Client struct { defaultMimeType string } -func NewClient(cfg config.GiteaConfig, respCache cache.ICache) (*Client, error) { - rootURL, err := url.Parse(cfg.Root) +func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error) { + // url.Parse returns valid on almost anything... + rootURL, err := url.ParseRequestURI(cfg.Root) if err != nil { - return nil, err + return nil, fmt.Errorf("invalid forgejo/gitea root url: %w", err) } - giteaRoot := strings.Trim(rootURL.String(), "/") + giteaRoot := strings.TrimSuffix(rootURL.String(), "/") stdClient := http.Client{Timeout: 10 * time.Second} diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index 4cb859a..0ae7962 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -13,7 +13,7 @@ import ( ) func TestHandlerPerformance(t *testing.T) { - cfg := config.GiteaConfig{ + cfg := config.ForgeConfig{ Root: "https://codeberg.org", Token: "", LFSEnabled: false, diff --git a/server/startup.go b/server/startup.go index fd89803..95c3c5c 100644 --- a/server/startup.go +++ b/server/startup.go @@ -77,7 +77,7 @@ func Serve(ctx *cli.Context) error { // clientResponseCache stores responses from the Gitea server clientResponseCache := cache.NewInMemoryCache() - giteaClient, err := gitea.NewClient(cfg.Gitea, clientResponseCache) + giteaClient, err := gitea.NewClient(cfg.Forge, clientResponseCache) if err != nil { return fmt.Errorf("could not create new gitea client: %v", err) } From b9a9467dba4ee3d2b2004339075c3a8b86c04fe5 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 26 May 2024 17:11:00 +0000 Subject: [PATCH 249/320] chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v4 (#332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-docker-buildx](https://codeberg.org/woodpecker-plugins/docker-buildx) ([source](https://codeberg.org/woodpecker-plugins/docker-buildx.git)) | major | `3.2.1` -> `4.0.0` | --- ### Release Notes
woodpecker-plugins/docker-buildx (woodpeckerci/plugin-docker-buildx) ### [`v4.0.0`](https://codeberg.org/woodpecker-plugins/docker-buildx/releases/tag/v4.0.0) [Compare Source](https://codeberg.org/woodpecker-plugins/docker-buildx/compare/v3.2.1...v4.0.0) - chore(deps): update docker docker tag to v26.1.0 - chore(deps): update docker/buildx-bin docker tag to v0.14.0 ([#​155](https://github.com/woodpecker-plugins/docker-buildx/issues/155))
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Co-authored-by: crapStone Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/332 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index a86c7fe..ea38374 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -29,7 +29,7 @@ steps: docker-dryrun: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:3.2.1 + image: woodpeckerci/plugin-docker-buildx:4.0.0 settings: dockerfile: Dockerfile platforms: linux/amd64 @@ -99,7 +99,7 @@ steps: docker-next: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:3.2.1 + image: woodpeckerci/plugin-docker-buildx:4.0.0 settings: registry: codeberg.org dockerfile: Dockerfile @@ -133,7 +133,7 @@ steps: docker-tag: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:3.2.1 + image: woodpeckerci/plugin-docker-buildx:4.0.0 settings: registry: codeberg.org dockerfile: Dockerfile From 2c41e11f2ff0565456f9b2d74c79d961266226c7 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 26 May 2024 20:05:46 +0000 Subject: [PATCH 250/320] Use hashicorp's LRU cache for DNS & certificates (#315) Taken from #301 Co-authored-by: Moritz Marquardt Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/315 --- flake.lock | 6 ++--- flake.nix | 1 + go.mod | 1 + go.sum | 2 ++ server/certificates/certificates.go | 34 +++++++++++++++++-------- server/dns/dns.go | 20 +++++++++------ server/handler/handler.go | 4 +-- server/handler/handler_custom_domain.go | 6 ++--- server/handler/handler_test.go | 2 +- server/startup.go | 7 ++--- 10 files changed, 50 insertions(+), 33 deletions(-) diff --git a/flake.lock b/flake.lock index b14ba48..c74fe33 100644 --- a/flake.lock +++ b/flake.lock @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1714030708, - "narHash": "sha256-JOGPOxa8N6ySzB7SQBsh0OVz+UXZriyahgvfNHMIY0Y=", + "lastModified": 1716715802, + "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b0d52b31f7f4d80f8bf38f0253652125579c35ff", + "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index f981ed1..61f3b55 100644 --- a/flake.nix +++ b/flake.nix @@ -16,6 +16,7 @@ gcc go gofumpt + golangci-lint gopls gotools go-tools diff --git a/go.mod b/go.mod index 518dff0..bb3a05a 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/creasty/defaults v1.7.0 github.com/go-acme/lego/v4 v4.5.3 github.com/go-sql-driver/mysql v1.6.0 + github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/joho/godotenv v1.4.0 github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 diff --git a/go.sum b/go.sum index ae24fc3..5875472 100644 --- a/go.sum +++ b/go.sum @@ -323,6 +323,8 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 67219dd..ff34775 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -14,6 +14,7 @@ import ( "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" + "github.com/hashicorp/golang-lru/v2/expirable" "github.com/reugn/equalizer" "github.com/rs/zerolog/log" @@ -31,11 +32,14 @@ func TLSConfig(mainDomainSuffix string, giteaClient *gitea.Client, acmeClient *AcmeClient, firstDefaultBranch string, - keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.ICache, + challengeCache, canonicalDomainCache cache.ICache, certDB database.CertDB, noDNS01 bool, rawDomain string, ) *tls.Config { + // every cert is at most 24h in the cache and 7 days before expiry the cert is renewed + keyCache := expirable.NewLRU[string, *tls.Certificate](32, nil, 24*time.Hour) + return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { @@ -86,7 +90,7 @@ func TLSConfig(mainDomainSuffix string, } } else { var targetRepo, targetBranch string - targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) + targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch) if targetOwner == "" { // DNS not set up, return main certificate to redirect to the docs domain = mainDomainSuffix @@ -107,7 +111,7 @@ func TLSConfig(mainDomainSuffix string, if tlsCertificate, ok := keyCache.Get(domain); ok { // we can use an existing certificate object - return tlsCertificate.(*tls.Certificate), nil + return tlsCertificate, nil } var tlsCertificate *tls.Certificate @@ -132,9 +136,8 @@ func TLSConfig(mainDomainSuffix string, } } - if err := keyCache.Set(domain, tlsCertificate, 15*time.Minute); err != nil { - return nil, err - } + keyCache.Add(domain, tlsCertificate) + return tlsCertificate, nil }, NextProtos: []string{ @@ -186,11 +189,10 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv // TODO: document & put into own function if !strings.EqualFold(sni, mainDomainSuffix) { - tlsCertificate.Leaf, err = x509.ParseCertificate(tlsCertificate.Certificate[0]) + tlsCertificate.Leaf, err = leaf(&tlsCertificate) if err != nil { - return nil, fmt.Errorf("error parsing leaf tlsCert: %w", err) + return nil, err } - // renew certificates 7 days before they expire if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { // TODO: use ValidTill of custom cert struct @@ -291,6 +293,7 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew } leaf, err := leaf(&tlsCertificate) if err == nil && leaf.NotAfter.After(time.Now()) { + tlsCertificate.Leaf = leaf // avoid sending a mock cert instead of a still valid cert, instead abuse CSR field to store time to try again at renew.CSR = []byte(strconv.FormatInt(time.Now().Add(6*time.Hour).Unix(), 10)) if err := keyDatabase.Put(name, renew); err != nil { @@ -388,11 +391,20 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *Acm } } -// leaf returns the parsed leaf certificate, either from c.leaf or by parsing +// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing // the corresponding c.Certificate[0]. +// After successfully parsing the cert c.Leaf gets set to the parsed cert. func leaf(c *tls.Certificate) (*x509.Certificate, error) { if c.Leaf != nil { return c.Leaf, nil } - return x509.ParseCertificate(c.Certificate[0]) + + leaf, err := x509.ParseCertificate(c.Certificate[0]) + if err != nil { + return nil, fmt.Errorf("tlsCert - failed to parse leaf: %w", err) + } + + c.Leaf = leaf + + return leaf, err } diff --git a/server/dns/dns.go b/server/dns/dns.go index 970f0c0..e29e42c 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -5,22 +5,26 @@ import ( "strings" "time" - "codeberg.org/codeberg/pages/server/cache" + "github.com/hashicorp/golang-lru/v2/expirable" ) -// lookupCacheTimeout specifies the timeout for the DNS lookup cache. -var lookupCacheTimeout = 15 * time.Minute +const ( + lookupCacheValidity = 30 * time.Second + defaultPagesRepo = "pages" +) -var defaultPagesRepo = "pages" +// TODO(#316): refactor to not use global variables +var lookupCache *expirable.LRU[string, string] = expirable.NewLRU[string, string](4096, nil, lookupCacheValidity) // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // If everything is fine, it returns the target data. -func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.ICache) (targetOwner, targetRepo, targetBranch string) { +func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string) (targetOwner, targetRepo, targetBranch string) { // Get CNAME or TXT var cname string var err error - if cachedName, ok := dnsLookupCache.Get(domain); ok { - cname = cachedName.(string) + + if entry, ok := lookupCache.Get(domain); ok { + cname = entry } else { cname, err = net.LookupCNAME(domain) cname = strings.TrimSuffix(cname, ".") @@ -38,7 +42,7 @@ func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLo } } } - _ = dnsLookupCache.Set(domain, cname, lookupCacheTimeout) + _ = lookupCache.Add(domain, cname) } if cname == "" { return diff --git a/server/handler/handler.go b/server/handler/handler.go index ffc3400..c038c2d 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -23,7 +23,7 @@ const ( func Handler( cfg config.ServerConfig, giteaClient *gitea.Client, - dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, + canonicalDomainCache, redirectsCache cache.ICache, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { log.Debug().Msg("\n----------------------------------------------------------") @@ -108,7 +108,7 @@ func Handler( trimmedHost, pathElements, cfg.PagesBranches[0], - dnsLookupCache, canonicalDomainCache, redirectsCache) + canonicalDomainCache, redirectsCache) } } } diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 82953f9..852001a 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -19,10 +19,10 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g trimmedHost string, pathElements []string, firstDefaultBranch string, - dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, + canonicalDomainCache, redirectsCache cache.ICache, ) { // Serve pages from custom domains - targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) + targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch) if targetOwner == "" { html.ReturnErrorPage(ctx, "could not obtain repo owner from custom domain", @@ -53,7 +53,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g return } else if canonicalDomain != trimmedHost { // only redirect if the target is also a codeberg page! - targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, firstDefaultBranch, dnsLookupCache) + targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, firstDefaultBranch) if targetOwner != "" { ctx.Redirect("https://"+canonicalDomain+"/"+targetOpt.TargetPath, http.StatusTemporaryRedirect) return diff --git a/server/handler/handler_test.go b/server/handler/handler_test.go index 0ae7962..765b3b1 100644 --- a/server/handler/handler_test.go +++ b/server/handler/handler_test.go @@ -29,7 +29,7 @@ func TestHandlerPerformance(t *testing.T) { AllowedCorsDomains: []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, PagesBranches: []string{"pages"}, } - testHandler := Handler(serverCfg, giteaClient, cache.NewInMemoryCache(), cache.NewInMemoryCache(), cache.NewInMemoryCache()) + testHandler := Handler(serverCfg, giteaClient, cache.NewInMemoryCache(), cache.NewInMemoryCache()) testCase := func(uri string, status int) { t.Run(uri, func(t *testing.T) { diff --git a/server/startup.go b/server/startup.go index 95c3c5c..6642d83 100644 --- a/server/startup.go +++ b/server/startup.go @@ -66,12 +66,9 @@ func Serve(ctx *cli.Context) error { } defer closeFn() - keyCache := cache.NewInMemoryCache() challengeCache := cache.NewInMemoryCache() // canonicalDomainCache stores canonical domains canonicalDomainCache := cache.NewInMemoryCache() - // dnsLookupCache stores DNS lookups for custom domains - dnsLookupCache := cache.NewInMemoryCache() // redirectsCache stores redirects in _redirects files redirectsCache := cache.NewInMemoryCache() // clientResponseCache stores responses from the Gitea server @@ -104,7 +101,7 @@ func Serve(ctx *cli.Context) error { giteaClient, acmeClient, cfg.Server.PagesBranches[0], - keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, + challengeCache, canonicalDomainCache, certDB, cfg.ACME.NoDNS01, cfg.Server.RawDomain, @@ -134,7 +131,7 @@ func Serve(ctx *cli.Context) error { } // Create ssl handler based on settings - sslHandler := handler.Handler(cfg.Server, giteaClient, dnsLookupCache, canonicalDomainCache, redirectsCache) + sslHandler := handler.Handler(cfg.Server, giteaClient, canonicalDomainCache, redirectsCache) // Start the ssl listener log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) From 74cdf78044f7f215654ac3cc6118f4f9e2f6cb60 Mon Sep 17 00:00:00 2001 From: adagio Date: Mon, 27 May 2024 15:00:10 +0000 Subject: [PATCH 251/320] Sorts the env var flags (#334) Applies a sort for the various env flags for ease of user lookup. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/334 Reviewed-by: crapStone Co-authored-by: adagio Co-committed-by: adagio --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 34143f1..98e16d9 100644 --- a/README.md +++ b/README.md @@ -69,23 +69,21 @@ This will trigger a build of the PR which will build a docker image to be used f ### Environment Variables +- `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. +- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. +- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. +- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. +- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. +- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. See for available values & additional environment variables. +- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. +- `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. +- `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. - `HOST` & `PORT` (default: `[::]` & `443`): listen address. +- `LOG_LEVEL` (default: warn): Set this to specify the level of logging. +- `NO_DNS_01` (default: `false`): Disable the use of ACME DNS. This means that the wildcard certificate is self-signed and all domains and subdomains will have a distinct certificate. Because this may lead to a rate limit from the ACME provider, this option is not recommended for Gitea/Forgejo instances with open registrations or a great number of users/orgs. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. - `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources (must be subdomain of `PAGES_DOMAIN`). -- `GITEA_ROOT` (default: `https://codeberg.org`): root of the upstream Gitea instance. -- `GITEA_API_TOKEN` (default: empty): API token for the Gitea instance to access non-public (e.g. limited) repos. - `RAW_INFO_PAGE` (default: ): info page for raw resources, shown if no resource is provided. -- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). - ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. -- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. -- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. -- `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. -- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. -- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. -- `DNS_PROVIDER` (default: use self-signed certificate): Code of the ACME DNS provider for the main domain wildcard. - See for available values & additional environment variables. -- `NO_DNS_01` (default: `false`): Disable the use of ACME DNS. This means that the wildcard certificate is self-signed and all domains and subdomains will have a distinct certificate. Because this may lead to a rate limit from the ACME provider, this option is not recommended for Gitea/Forgejo instances with open registrations or a great number of users/orgs. -- `LOG_LEVEL` (default: warn): Set this to specify the level of logging. ## Contributing to the development From 3928cd7affb31980fa77dfd984a4a17b373365da Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Wed, 29 May 2024 00:33:03 +0000 Subject: [PATCH 252/320] fix(deps): update golang.org/x/exp digest to 4c93da0 (#344) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bb3a05a..e0bd51c 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 5875472..01c1e1b 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d h1:N0hmiNbwsSNwHBAvR3QB5w25pUwH4tK0Y/RltD1j1h4= +golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From 05413e50c6780969b760849ccfac16b0f17d8d8f Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Thu, 30 May 2024 00:43:46 +0000 Subject: [PATCH 253/320] chore(deps): update golangci/golangci-lint docker tag to v1.59.0 (#345) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index cf59edc..7e7ba51 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -8,7 +8,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.58.2 + image: golangci/golangci-lint:v1.59.0 commands: - go version - go install mvdan.cc/gofumpt@latest From 4543033874424431a96166d39fd187f9ee20e675 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 2 Jun 2024 01:02:45 +0000 Subject: [PATCH 254/320] fix(deps): update golang.org/x/exp digest to fd00a4e (#346) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e0bd51c..7ae5a5b 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d + golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 01c1e1b..271416f 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d h1:N0hmiNbwsSNwHBAvR3QB5w25pUwH4tK0Y/RltD1j1h4= -golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= +golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From e61ab7075ba92bb3051e2fc0db5e8816228455c7 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 9 Jun 2024 01:17:54 +0000 Subject: [PATCH 255/320] fix(deps): update golang.org/x/exp digest to fc45aab (#352) --- go.mod | 2 +- go.sum | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 7ae5a5b..b821614 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc + golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 271416f..bc63665 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= -golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= +golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -810,8 +810,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1004,8 +1004,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 2c1701a657bbdd615b1fac10775026c92da47cd2 Mon Sep 17 00:00:00 2001 From: fnetX Date: Thu, 13 Jun 2024 13:31:27 +0000 Subject: [PATCH 256/320] Remove RAW_INFO_PAGE from README (#353) https://codeberg.org/Codeberg/pages-server/pulls/261 removed that option, but not from the README. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/353 Co-authored-by: fnetX Co-committed-by: fnetX --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 98e16d9..029ba9b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,6 @@ This will trigger a build of the PR which will build a docker image to be used f - `NO_DNS_01` (default: `false`): Disable the use of ACME DNS. This means that the wildcard certificate is self-signed and all domains and subdomains will have a distinct certificate. Because this may lead to a rate limit from the ACME provider, this option is not recommended for Gitea/Forgejo instances with open registrations or a great number of users/orgs. - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. - `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources (must be subdomain of `PAGES_DOMAIN`). -- `RAW_INFO_PAGE` (default: ): info page for raw resources, shown if no resource is provided. ## Contributing to the development From adcd973ade505e5b19673001335b1fd8263c2533 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 16 Jun 2024 01:04:41 +0000 Subject: [PATCH 257/320] chore(deps): update golangci/golangci-lint docker tag to v1.59.1 (#357) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 7e7ba51..a859606 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -8,7 +8,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.59.0 + image: golangci/golangci-lint:v1.59.1 commands: - go version - go install mvdan.cc/gofumpt@latest From c043a887c22e15528452ec82d7ccf9119a9dbb6a Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 17 Jun 2024 00:53:42 +0000 Subject: [PATCH 258/320] fix(deps): update golang.org/x/exp digest to 7f521ea (#356) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b821614..9a4c67c 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index bc63665..2a87883 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= -golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From 4e44ea1d58521f001706ddca057136fc44669ff4 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 7 Jul 2024 01:09:14 +0000 Subject: [PATCH 259/320] chore(deps): update pipelinecomponents/yamllint docker tag to v0.31.3 (#361) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index a859606..25c8354 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -28,7 +28,7 @@ steps: branch: renovate/* yamllint: - image: pipelinecomponents/yamllint:0.31.2 + image: pipelinecomponents/yamllint:0.31.3 depends_on: [] commands: - yamllint . From d583587773781be1a660c4512e303a33fde5bf05 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 8 Jul 2024 00:52:53 +0000 Subject: [PATCH 260/320] chore(deps): update mstruebing/editorconfig-checker docker tag to v3.0.3 (#360) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 25c8354..0e3df1d 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -21,7 +21,7 @@ steps: editor-config: depends_on: [] - image: mstruebing/editorconfig-checker:v3.0.1 + image: mstruebing/editorconfig-checker:v3.0.3 when: - event: pull_request - event: push From 17530a065b7e3c8b862f0fef5efcca942ab1d1ed Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 14 Jul 2024 01:21:11 +0000 Subject: [PATCH 261/320] fix(deps): update golang.org/x/exp digest to 46b0784 (#363) --- go.mod | 2 +- go.sum | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 9a4c67c..7e71146 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 2a87883..d1acb45 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -810,8 +810,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1004,8 +1004,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From bc9111a05fef91e6912d068f3c4d50b4e1d244cd Mon Sep 17 00:00:00 2001 From: Peter Gerber Date: Tue, 23 Jul 2024 18:42:24 +0000 Subject: [PATCH 262/320] Use correct timestamp format for Last-Modified header (#365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTTP uses GMT [1,2] rather than UTC as timezone for timestamps. However, the Last-Modified header used UTC which confused at least wget. Before, UTC was used: $ wget --no-check-certificate -S --spider https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg ... Last-Modified: Sun, 11 Sep 2022 08:37:42 UTC ... Last-modified header invalid -- time-stamp ignored. ... After, GMT is used: $ wget --no-check-certificate -S --spider https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg ... Last-Modified: Sun, 11 Sep 2022 08:37:42 GMT ... (no last-modified-header-invalid warning) [1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified [2]: https://www.rfc-editor.org/rfc/rfc9110#name-date-time-formats Fixes #364 --- Whatt I noticed is that the If-Modified-Since header isn't accepted (neither with GMT nor with UTC): ``` $ wget --header "If-Modified-Since: Sun, 11 Sep 2022 08:37:42 GMT" --no-check-certificate -S --spider https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg Spider mode enabled. Check if remote file exists. --2024-07-15 23:31:41-- https://cb_pages_tests.localhost.mock.directory:4430/images/827679288a.jpg Resolving cb_pages_tests.localhost.mock.directory (cb_pages_tests.localhost.mock.directory)... 127.0.0.1 Connecting to cb_pages_tests.localhost.mock.directory (cb_pages_tests.localhost.mock.directory)|127.0.0.1|:4430... connected. WARNING: The certificate of ‘cb_pages_tests.localhost.mock.directory’ is not trusted. WARNING: The certificate of ‘cb_pages_tests.localhost.mock.directory’ doesn't have a known issuer. HTTP request sent, awaiting response... HTTP/1.1 200 OK Allow: GET, HEAD, OPTIONS Cache-Control: public, max-age=600 Content-Length: 124635 Content-Type: image/jpeg Etag: "073af1960852e2a4ef446202c7974768b9881814" Last-Modified: Sun, 11 Sep 2022 08:37:42 GMT Referrer-Policy: strict-origin-when-cross-origin Server: pages-server Strict-Transport-Security: max-age=63072000; includeSubdomains; preload Date: Mon, 15 Jul 2024 21:31:42 GMT Length: 124635 (122K) [image/jpeg] Remote file exists ``` I would have expected a 304 (Not Modified) rather than a 200 (OK). I assume this is simply not supported and on production 304 is returned by a caching proxy in front of pages-server. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/365 Reviewed-by: crapStone Co-authored-by: Peter Gerber Co-committed-by: Peter Gerber --- server/upstream/header.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/upstream/header.go b/server/upstream/header.go index 9575a3f..7b85df1 100644 --- a/server/upstream/header.go +++ b/server/upstream/header.go @@ -24,5 +24,5 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) { } else { ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) } - ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(time.RFC1123)) + ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat)) } From efd1adae0ff65b1b908181dd8e1ee47f09b23108 Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 23 Jul 2024 19:50:54 +0000 Subject: [PATCH 263/320] fix woodpecker pipeline definition (#370) use https://woodpecker-ci.org/plugins/Release instead of the old drone plugin Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/370 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index ea38374..bff5c9b 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -18,6 +18,7 @@ steps: build: depends_on: vendor image: codeberg.org/6543/docker-images/golang_just + pull: true commands: - go version - just build @@ -45,6 +46,7 @@ steps: build-tag: depends_on: vendor image: codeberg.org/6543/docker-images/golang_just + pull: true commands: - go version - just build-tag ${CI_COMMIT_TAG##v} @@ -56,6 +58,7 @@ steps: test: depends_on: build image: codeberg.org/6543/docker-images/golang_just + pull: true commands: - just test when: @@ -66,6 +69,7 @@ steps: integration-tests: depends_on: build image: codeberg.org/6543/docker-images/golang_just + pull: true commands: - just integration environment: @@ -80,18 +84,13 @@ steps: release: depends_on: build - image: plugins/gitea-release:1.1.0 + image: woodpeckerci/plugin-release:0.1.0 settings: base_url: https://codeberg.org file_exists: overwrite files: build/codeberg-pages-server api_key: from_secret: bot_token - environment: - - CI_REPO_OWNER=${CI_REPO_OWNER} - - CI_REPO_NAME=${CI_REPO_NAME} - - CI_BUILD_EVENT=${CI_BUILD_EVENT} - - CI_COMMIT_REF=${CI_COMMIT_REF} when: - event: ['tag'] branch: From 6ce17461e6a6feb88cd95fd808d24ac61ba34daa Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 23 Jul 2024 20:01:02 +0000 Subject: [PATCH 264/320] chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v4.2.0 (#362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-docker-buildx](https://codeberg.org/woodpecker-plugins/docker-buildx) ([source](https://codeberg.org/woodpecker-plugins/docker-buildx.git)) | minor | `4.0.0` -> `4.2.0` | | [woodpeckerci/plugin-docker-buildx](https://codeberg.org/woodpecker-plugins/docker-buildx) ([source](https://codeberg.org/woodpecker-plugins/docker-buildx.git)) | major | `3.2.1` -> `4.2.0` | --- ### Release Notes
woodpecker-plugins/docker-buildx (woodpeckerci/plugin-docker-buildx) ### [`v4.2.0`](https://codeberg.org/woodpecker-plugins/docker-buildx/releases/tag/v4.2.0) [Compare Source](https://codeberg.org/woodpecker-plugins/docker-buildx/compare/v4.1.0...v4.2.0) ##### Features - Add dedicated setting to automatically handle proxy configuration ([#​171](https://github.com/woodpecker-plugins/docker-buildx/issues/171)) ##### Dependencies - chore(deps): update docker/buildx-bin docker tag to v0.16.0 - fix(deps): update module github.com/aws/aws-sdk-go to v1.54.19 ### [`v4.1.0`](https://codeberg.org/woodpecker-plugins/docker-buildx/releases/tag/v4.1.0) [Compare Source](https://codeberg.org/woodpecker-plugins/docker-buildx/compare/v4.0.0...v4.1.0) ##### Features - Pass Daemon.Mirror to Docker daemon ([#​165](https://github.com/woodpecker-plugins/docker-buildx/issues/165)) ##### Bug Fixes - Respect repository mirrors without login credentials ([#​164](https://github.com/woodpecker-plugins/docker-buildx/issues/164)) ##### Dependencies - chore(deps): update docker/buildx-bin docker tag to v0.15.1 - chore(deps): update docker docker tag to v26.1.4
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/362 Reviewed-by: crapStone Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index bff5c9b..a00b7d6 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -30,7 +30,7 @@ steps: docker-dryrun: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.0.0 + image: woodpeckerci/plugin-docker-buildx:4.2.0 settings: dockerfile: Dockerfile platforms: linux/amd64 @@ -98,7 +98,7 @@ steps: docker-next: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.0.0 + image: woodpeckerci/plugin-docker-buildx:4.2.0 settings: registry: codeberg.org dockerfile: Dockerfile @@ -114,7 +114,7 @@ steps: branch: ${CI_REPO_DEFAULT_BRANCH} 'Publish PR image': - image: woodpeckerci/plugin-docker-buildx:3.2.1 + image: woodpeckerci/plugin-docker-buildx:4.2.0 depends_on: test settings: registry: codeberg.org @@ -132,7 +132,7 @@ steps: docker-tag: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.0.0 + image: woodpeckerci/plugin-docker-buildx:4.2.0 settings: registry: codeberg.org dockerfile: Dockerfile From abbebbbceef335d251608b1dfe9e831af19eef43 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 23 Jul 2024 20:19:44 +0000 Subject: [PATCH 265/320] fix(deps): update golang.org/x/exp digest to 8a7402a (#368) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7e71146..479d6a5 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240707233637-46b078467d37 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index d1acb45..9d9c871 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= -golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From 68825a17272224efca0a15544cf7608fe0429272 Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 23 Jul 2024 20:32:46 +0000 Subject: [PATCH 266/320] improve pipeline config (#371) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/371 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 35 ++++++++--------------------------- .woodpecker/lint.yml | 14 -------------- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index a00b7d6..9bf20f9 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -1,9 +1,6 @@ when: - - event: [pull_request, tag, cron] - - event: push - branch: - - ${CI_REPO_DEFAULT_BRANCH} - - renovate/* + - event: [push, pull_request, tag, cron] + branch: ${CI_REPO_DEFAULT_BRANCH} depends_on: - lint @@ -24,9 +21,6 @@ steps: - just build when: - event: [push, pull_request] - branch: - - ${CI_REPO_DEFAULT_BRANCH} - - renovate/* docker-dryrun: depends_on: vendor @@ -38,9 +32,6 @@ steps: tags: latest when: - event: [push, pull_request] - branch: - - ${CI_REPO_DEFAULT_BRANCH} - - renovate/* path: Dockerfile build-tag: @@ -51,9 +42,7 @@ steps: - go version - just build-tag ${CI_COMMIT_TAG##v} when: - - event: ['tag'] - branch: - - ${CI_REPO_DEFAULT_BRANCH} + - event: [tag] test: depends_on: build @@ -62,9 +51,7 @@ steps: commands: - just test when: - - event: pull_request - - event: push - branch: renovate/* + - event: [push, pull_request] integration-tests: depends_on: build @@ -78,9 +65,7 @@ steps: - RAW_DOMAIN=raw.localhost.mock.directory - PORT=4430 when: - - event: pull_request - - event: push - branch: renovate/* + - event: [push, pull_request] release: depends_on: build @@ -92,9 +77,7 @@ steps: api_key: from_secret: bot_token when: - - event: ['tag'] - branch: - - ${CI_REPO_DEFAULT_BRANCH} + - event: [tag] docker-next: depends_on: vendor @@ -110,8 +93,7 @@ steps: password: from_secret: bot_token when: - - event: ['push'] - branch: ${CI_REPO_DEFAULT_BRANCH} + - event: [push] 'Publish PR image': image: woodpeckerci/plugin-docker-buildx:4.2.0 @@ -144,5 +126,4 @@ steps: password: from_secret: bot_token when: - - event: ['push'] - branch: ${CI_REPO_DEFAULT_BRANCH} + - event: [push] diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 0e3df1d..5699a0a 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -1,9 +1,7 @@ when: - event: pull_request - - event: push branch: - ${CI_REPO_DEFAULT_BRANCH} - - renovate/** steps: lint: @@ -14,28 +12,16 @@ steps: - go install mvdan.cc/gofumpt@latest - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - golangci-lint run --timeout 5m --build-tags integration - when: - - event: pull_request - - event: push - branch: renovate/* editor-config: depends_on: [] image: mstruebing/editorconfig-checker:v3.0.3 - when: - - event: pull_request - - event: push - branch: renovate/* yamllint: image: pipelinecomponents/yamllint:0.31.3 depends_on: [] commands: - yamllint . - when: - - event: pull_request - - event: push - branch: renovate/* prettier: image: docker.io/woodpeckerci/plugin-prettier:0.1.0 From 5265b3884b5f05e9000abeb7d6fc63a69c2b9b5c Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 23 Jul 2024 22:58:34 +0200 Subject: [PATCH 267/320] don't depend on lint for push to main --- .woodpecker/build.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 9bf20f9..eb6af24 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -2,9 +2,6 @@ when: - event: [push, pull_request, tag, cron] branch: ${CI_REPO_DEFAULT_BRANCH} -depends_on: - - lint - steps: # use vendor to cache dependencies vendor: @@ -31,7 +28,7 @@ steps: dry-run: true tags: latest when: - - event: [push, pull_request] + - event: [pull_request] path: Dockerfile build-tag: @@ -51,7 +48,7 @@ steps: commands: - just test when: - - event: [push, pull_request] + - event: [pull_request] integration-tests: depends_on: build @@ -65,7 +62,7 @@ steps: - RAW_DOMAIN=raw.localhost.mock.directory - PORT=4430 when: - - event: [push, pull_request] + - event: [pull_request] release: depends_on: build From c9be1ce75dd3c9efefd8ba92e327ad6ecba2d211 Mon Sep 17 00:00:00 2001 From: crapStone Date: Tue, 23 Jul 2024 21:18:06 +0000 Subject: [PATCH 268/320] fix docker latest tagging (#372) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/372 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index eb6af24..23d5d1d 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -109,7 +109,7 @@ steps: evaluate: 'CI_COMMIT_PULL_REQUEST_LABELS contains "build_pr_image"' event: pull_request - docker-tag: + docker-release: depends_on: vendor image: woodpeckerci/plugin-docker-buildx:4.2.0 settings: @@ -123,4 +123,4 @@ steps: password: from_secret: bot_token when: - - event: [push] + - event: [tag] From 9524b1eb12f77fa345cc8a220f67ae244da0ab12 Mon Sep 17 00:00:00 2001 From: Gnaaarwhal Date: Fri, 2 Aug 2024 12:52:44 +0000 Subject: [PATCH 269/320] Fix typo in README.md (#374) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/374 Reviewed-by: crapStone Co-authored-by: Gnaaarwhal Co-committed-by: Gnaaarwhal --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 029ba9b..0051a9e 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ This will trigger a build of the PR which will build a docker image to be used f ### Environment Variables - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. -- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. +- `ACME_API` (default: ): set this to to use invalid certificates without any verification (great for debugging). ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. From 6cff8d2ee922f1245de76b45c6d8bb7c6d79ea2e Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 19 Aug 2024 20:19:04 +0000 Subject: [PATCH 270/320] chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.1 (#376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-release](https://codeberg.org/woodpecker-plugins/release) ([source](https://codeberg.org/woodpecker-plugins/release.git)) | minor | `0.1.0` -> `0.2.1` | --- ### Release Notes
woodpecker-plugins/release (woodpeckerci/plugin-release) ### [`v0.2.1`](https://codeberg.org/woodpecker-plugins/release/releases/tag/v0.2.1) [Compare Source](https://codeberg.org/woodpecker-plugins/release/compare/v0.2.0...v0.2.1) - Fix infinite loop ([#​13](https://github.com/woodpecker-plugins/release/issues/13)) ### [`v0.2.0`](https://codeberg.org/woodpecker-plugins/release/releases/tag/v0.2.0) [Compare Source](https://codeberg.org/woodpecker-plugins/release/compare/v0.1.0...v0.2.0) ### Bugfix - Fix loading without releases ([#​9](https://github.com/woodpecker-plugins/release/issues/9))
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/376 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 23d5d1d..9270c33 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -66,7 +66,7 @@ steps: release: depends_on: build - image: woodpeckerci/plugin-release:0.1.0 + image: woodpeckerci/plugin-release:0.2.1 settings: base_url: https://codeberg.org file_exists: overwrite From c76daaca4d4258f6939e53382fc45d0bd0905e77 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 19 Aug 2024 20:20:07 +0000 Subject: [PATCH 271/320] chore(deps): update golang docker tag to v1.23 (#380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | golang | minor | `1.22` -> `1.23` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/380 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 9270c33..976e923 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -5,7 +5,7 @@ when: steps: # use vendor to cache dependencies vendor: - image: golang:1.22 + image: golang:1.23 commands: - go mod vendor From 557a29573296dd629e642f9d37179c271c50ed23 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 19 Aug 2024 20:20:59 +0000 Subject: [PATCH 272/320] chore(deps): update golangci/golangci-lint docker tag to v1.60.1 (#381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | minor | `v1.59.1` -> `v1.60.1` | --- ### Release Notes
golangci/golangci-lint (golangci/golangci-lint) ### [`v1.60.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1601) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.59.1...v1.60.1) 1. Updated linters - `errorlint`: from 1.5.2 to 1.6.0 - `exhaustruct`: from 3.2.0 to 3.3.0 (recognize custom error values in return) - `fatcontext`: from 0.2.2 to 0.4.0 (fix false positives for context stored in structs) - `gocognit`: from 1.1.2 to 1.1.3 - `gomodguard`: from 1.3.2 to 1.3.3 - `govet` (`printf`): report non-constant format, no args - `lll`: advertise max line length instead of just reporting failure - `revive`: from 1.3.7 to 1.3.9 (new rule: `comments-density`) - `sloglint`: from 0.7.1 to 0.7.2 - `spancheck`: from 0.6.1 to 0.6.2 - `staticcheck`: from 0.4.7 to 0.5.0 - `tenv`: from 1.7.1 to 1.10.0 (remove reports on fuzzing) - `testifylint`: from 1.3.1 to 1.4.3 (new options: `formatter`, `suite-broken-parallel`, `suite-subtest-run`) - `tparallel`: from 0.3.1 to 0.3.2 - `usestdlibvars`: from 1.26.0 to 1.27.0 (fix false-positive with number used inside a mathematical operations) - `wsl`: from 4.2.1 to 4.4.1 - ️⚠️ `unused`: remove `exported-is-used` option 2. Fixes - SARIF: sanitize level property - ️⚠️ `typecheck` issues should never be ignored 3. Documentation - Add link on linter without configuration - Remove 'trusted by' page - `wsl` update documentation of the configuration 4. misc. - 🎉 go1.23 support
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/381 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 5699a0a..1f281a4 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.59.1 + image: golangci/golangci-lint:v1.60.1 commands: - go version - go install mvdan.cc/gofumpt@latest From 2d4152d18b0b42c513efea6f2a3c7ff17bffaaf5 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Sep 2024 20:48:31 +0000 Subject: [PATCH 273/320] chore(deps): update golangci/golangci-lint docker tag to v1.61.0 (#383) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | minor | `v1.60.1` -> `v1.61.0` | --- ### Release Notes
golangci/golangci-lint (golangci/golangci-lint) ### [`v1.61.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1610) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.60.3...v1.61.0) 1. Enhancements - Add `junit-xml-extended` format - Exclude Swagger Codegen files by default 2. Updated linters - `dupword`: from 0.0.14 to 0.1.1 - `fatcontext`: from 0.4.0 to 0.5.2 - `gci`: from 0.13.4 to 0.13.5 (new option `no-lex-order`) - `go-ruleguard`: from 0.4.2 to [`0fe6f58`](https://github.com/golangci/golangci-lint/commit/0fe6f58b47b1) (fix panic with custom linters) - `godot`: from 1.4.16 to 1.4.17 - `gomodguard`: from 1.3.3 to 1.3.5 - `gosec`: disable temporarily `G407` - `gosec`: from [`ab3f6c1`](https://github.com/golangci/golangci-lint/commit/ab3f6c1c83a0) to 2.21.2 (partially fix `G115`) - `intrange`: from 0.1.2 to 0.2.0 - `nolintlint`: remove the empty line in the directive replacement 3. Misc. - Improve runtime version parsing 4. Documentation - Add additional info about `typecheck` ### [`v1.60.3`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1603) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.60.2...v1.60.3) 1. Updated linters - `gosec`: from [`81cda2f`](https://github.com/golangci/golangci-lint/commit/81cda2f91fbe) to [`ab3f6c1`](https://github.com/golangci/golangci-lint/commit/ab3f6c1c83a0) (fix `G115` false positives) 2. Misc. - Check that the Go version use to build is greater or equals to the Go version of the project ### [`v1.60.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1602) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.60.1...v1.60.2) 1. Updated linters - `gofmt`: update to HEAD (go1.22) - `gofumpt`: from 0.6.0 to 0.7.0 - `gosec`: fix G602 analyzer - `gosec`: from [`5f0084e`](https://github.com/golangci/golangci-lint/commit/5f0084eb01a9) to [`81cda2f`](https://github.com/golangci/golangci-lint/commit/81cda2f91fbe) (adds `G115`, `G405`, `G406`, `G506`, `G507`) - `staticcheck`: from 0.5.0 to 0.5.1 - `staticcheck`: propagate Go version - `wrapcheck`: from 2.8.3 to 2.9.0 - ⚠️ `exportloopref`: deprecation
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/383 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 1f281a4..6ba262c 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.60.1 + image: golangci/golangci-lint:v1.61.0 commands: - go version - go install mvdan.cc/gofumpt@latest From 549377aa01922dd19cefc1c849f4fb5550a59d8b Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Sep 2024 20:49:56 +0000 Subject: [PATCH 274/320] chore(deps): update docker.io/woodpeckerci/plugin-prettier docker tag to v0.2.0 (#385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [docker.io/woodpeckerci/plugin-prettier](https://github.com/woodpecker-ci/plugin-prettier) | minor | `0.1.0` -> `0.2.0` | --- ### Release Notes
woodpecker-ci/plugin-prettier (docker.io/woodpeckerci/plugin-prettier) ### [`v0.2.0`](https://github.com/woodpecker-ci/plugin-prettier/blob/HEAD/CHANGELOG.md#020---2024-08-26) [Compare Source](https://github.com/woodpecker-ci/plugin-prettier/compare/0.1.0...0.2.0) ##### ❤️ Thanks to all contributors! ❤️ [@​6543](https://github.com/6543), [@​qwerty287](https://github.com/qwerty287) ##### 📈 Enhancement - Allow to read version from lockfile \[[#​20](https://github.com/woodpecker-ci/plugin-prettier/pull/20)] ##### Misc - Update pipeline conf \[[#​41](https://github.com/woodpecker-ci/plugin-prettier/pull/41)] - \[pre-commit.ci] pre-commit autoupdate \[[#​40](https://github.com/woodpecker-ci/plugin-prettier/pull/40)] - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.2.0 \[[#​39](https://github.com/woodpecker-ci/plugin-prettier/pull/39)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v4.2.0 \[[#​38](https://github.com/woodpecker-ci/plugin-prettier/pull/38)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v4.1.0 \[[#​37](https://github.com/woodpecker-ci/plugin-prettier/pull/37)] - Update pre-commit hook igorshubovych/markdownlint-cli to v0.41.0 \[[#​34](https://github.com/woodpecker-ci/plugin-prettier/pull/34)] - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.2 \[[#​33](https://github.com/woodpecker-ci/plugin-prettier/pull/33)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v4 \[[#​32](https://github.com/woodpecker-ci/plugin-prettier/pull/32)] - Update pre-commit hook igorshubovych/markdownlint-cli to v0.40.0 \[[#​30](https://github.com/woodpecker-ci/plugin-prettier/pull/30)] - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.1 \[[#​29](https://github.com/woodpecker-ci/plugin-prettier/pull/29)] - Update pre-commit hook pre-commit/pre-commit-hooks to v4.6.0 \[[#​28](https://github.com/woodpecker-ci/plugin-prettier/pull/28)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.2.1 \[[#​27](https://github.com/woodpecker-ci/plugin-prettier/pull/27)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.2.0 \[[#​26](https://github.com/woodpecker-ci/plugin-prettier/pull/26)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.1.0 \[[#​25](https://github.com/woodpecker-ci/plugin-prettier/pull/25)] - \[pre-commit.ci] pre-commit autoupdate \[[#​24](https://github.com/woodpecker-ci/plugin-prettier/pull/24)] - Update woodpeckerci/plugin-docker-buildx Docker tag to v3.0.1 \[[#​23](https://github.com/woodpecker-ci/plugin-prettier/pull/23)] - Update woodpeckerci/plugin-ready-release-go Docker tag to v1.1.0 \[[#​22](https://github.com/woodpecker-ci/plugin-prettier/pull/22)]
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/385 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 6ba262c..403ded7 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -24,7 +24,7 @@ steps: - yamllint . prettier: - image: docker.io/woodpeckerci/plugin-prettier:0.1.0 + image: docker.io/woodpeckerci/plugin-prettier:0.2.0 depends_on: [] settings: version: 3.2.5 From e5320e1972b8abe83e4cbd09b4bf334ca04fb283 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Sep 2024 20:50:14 +0000 Subject: [PATCH 275/320] chore(deps): update pipelinecomponents/yamllint docker tag to v0.32.1 (#389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [pipelinecomponents/yamllint](https://gitlab.com/pipeline-components/yamllint) | minor | `0.31.3` -> `0.32.1` | --- ### Release Notes
pipeline-components/yamllint (pipelinecomponents/yamllint) ### [`v0.32.1`](https://gitlab.com/pipeline-components/yamllint/compare/v0.32.0...v0.32.1) [Compare Source](https://gitlab.com/pipeline-components/yamllint/compare/v0.32.0...v0.32.1) ### [`v0.32.0`](https://gitlab.com/pipeline-components/yamllint/compare/v0.31.3...v0.32.0) [Compare Source](https://gitlab.com/pipeline-components/yamllint/compare/v0.31.3...v0.32.0)
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/389 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 403ded7..3b7ca40 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -18,7 +18,7 @@ steps: image: mstruebing/editorconfig-checker:v3.0.3 yamllint: - image: pipelinecomponents/yamllint:0.31.3 + image: pipelinecomponents/yamllint:0.32.1 depends_on: [] commands: - yamllint . From 5b120f0488182e1a2d47a32b2fff93bdfa1e68cd Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 29 Sep 2024 21:00:54 +0000 Subject: [PATCH 276/320] Implement static serving of compressed files (#387) This provides an option for #223 without fully resolving it. (I think.) Essentially, it acts very similar to the `gzip_static` and similar options for nginx, where it will check for the existence of pre-compressed files and serve those instead if the client allows it. I couldn't find a pre-existing way to actually parse the Accept-Encoding header properly (admittedly didn't look very hard) and just implemented one on my own that should be fine. This should hopefully not have the same DOS vulnerabilities as #302, since it relies on the existing caching system. Compressed versions of files will be cached just like any other files, and that includes cache for missing files as well. The compressed files will also be accessible directly, and this won't automatically decompress them. So, if you have a `tar.gz` file that you access directly, it will still be downloaded as the gzipped version, although you will now gain the option to download the `.tar` directly and decompress it in transit. (Which doesn't affect the server at all, just the client's way of interpreting it.) ---- One key thing this change also adds is a short-circuit when accessing directories: these always return 404 via the API, although they'd try the cache anyway and go through that route, which was kind of slow. Adding in the additional encodings, it's going to try for .gz, .br, and .zst files in the worst case as well, which feels wrong. So, instead, it just always falls back to the index-check behaviour if the path ends in a slash or is empty. (Which is implicitly just a slash.) ---- For testing, I set up this repo: https://codeberg.org/clarfonthey/testrepo I ended up realising that LFS wasn't supported by default with `just dev`, so, it ended up working until I made sure the files on the repo *didn't* use LFS. Assuming you've run `just dev`, you can go directly to this page in the browser here: https://clarfonthey.localhost.mock.directory:4430/testrepo/ And also you can try a few cURL commands: ```shell curl https://clarfonthey.localhost.mock.directory:4430/testrepo/ --verbose --insecure curl -H 'Accept-Encoding: gz' https://clarfonthey.localhost.mock.directory:4430/testrepo/ --verbose --insecure | gunzip - curl -H 'Accept-Encoding: br' https://clarfonthey.localhost.mock.directory:4430/testrepo/ --verbose --insecure | brotli --decompress - curl -H 'Accept-Encoding: zst' https://clarfonthey.localhost.mock.directory:4430/testrepo/ --verbose --insecure | zstd --decompress - ``` Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/387 Reviewed-by: Gusted Co-authored-by: ltdk Co-committed-by: ltdk --- server/certificates/certificates.go | 2 +- server/gitea/cache.go | 18 +++-- server/gitea/client.go | 47 +++++++++---- server/upstream/header.go | 3 + server/upstream/upstream.go | 101 +++++++++++++++++++++++++++- 5 files changed, 150 insertions(+), 21 deletions(-) diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index ff34775..062f721 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -196,7 +196,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv // renew certificates 7 days before they expire if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { // TODO: use ValidTill of custom cert struct - if res.CSR != nil && len(res.CSR) > 0 { + if len(res.CSR) > 0 { // CSR stores the time when the renewal shall be tried again nextTryUnix, err := strconv.ParseInt(string(res.CSR), 10, 64) if err == nil && time.Now().Before(time.Unix(nextTryUnix, 0)) { diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 97024a1..cfb7c2a 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -37,15 +37,20 @@ type FileResponse struct { Exists bool IsSymlink bool ETag string - MimeType string - Body []byte + + // uncompressed MIME type + MimeType string + + // raw MIME type (if compressed, type of compression) + RawMime string + Body []byte } func (f FileResponse) IsEmpty() bool { return len(f.Body) == 0 } -func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) { +func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (header http.Header, statusCode int) { header = make(http.Header) if f.Exists { @@ -58,7 +63,12 @@ func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, s header.Set(giteaObjectTypeHeader, objTypeSymlink) } header.Set(ETagHeader, f.ETag) - header.Set(ContentTypeHeader, f.MimeType) + + if decompress { + header.Set(ContentTypeHeader, f.MimeType) + } else { + header.Set(ContentTypeHeader, f.RawMime) + } header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body))) header.Set(PagesCacheIndicatorHeader, "true") diff --git a/server/gitea/client.go b/server/gitea/client.go index 3abb487..9777043 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -39,9 +39,10 @@ const ( objTypeSymlink = "symlink" // std - ETagHeader = "ETag" - ContentTypeHeader = "Content-Type" - ContentLengthHeader = "Content-Length" + ETagHeader = "ETag" + ContentTypeHeader = "Content-Type" + ContentLengthHeader = "Content-Length" + ContentEncodingHeader = "Content-Encoding" ) type Client struct { @@ -103,7 +104,7 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) + reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false) if err != nil { return nil, err } @@ -111,21 +112,21 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str return io.ReadAll(reader) } -func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) { +func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) log := log.With().Str("cache_key", cacheKey).Logger() log.Trace().Msg("try file in cache") // handle if cache entry exist if cache, ok := client.responseCache.Get(cacheKey); ok { cache := cache.(FileResponse) - cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) + cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress) // TODO: check against some timestamp mismatch?!? if cache.Exists { log.Debug().Msg("[cache] exists") if cache.IsSymlink { linkDest := string(cache.Body) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) } else if !cache.IsEmpty() { log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil @@ -170,13 +171,17 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str } log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) } } // now we are sure it's content so set the MIME type - mimeType := client.getMimeTypeByExtension(resource) - resp.Response.Header.Set(ContentTypeHeader, mimeType) + mimeType, rawType := client.getMimeTypeByExtension(resource) + if decompress { + resp.Response.Header.Set(ContentTypeHeader, mimeType) + } else { + resp.Response.Header.Set(ContentTypeHeader, rawType) + } if !shouldRespBeSavedToCache(resp.Response) { return reader, resp.Response.Header, resp.StatusCode, err @@ -187,6 +192,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str Exists: true, ETag: resp.Header.Get(ETagHeader), MimeType: mimeType, + RawMime: rawType, } return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil @@ -300,16 +306,31 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) { return false, nil } -func (client *Client) getMimeTypeByExtension(resource string) string { - mimeType := mime.TypeByExtension(path.Ext(resource)) +func (client *Client) extToMime(ext string) string { + mimeType := mime.TypeByExtension(ext) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { mimeType = client.defaultMimeType } - log.Trace().Msgf("probe mime of %q is %q", resource, mimeType) return mimeType } +func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) { + rawExt := path.Ext(resource) + innerExt := rawExt + switch rawExt { + case ".gz", ".br", ".zst": + innerExt = path.Ext(resource[:len(resource)-len(rawExt)]) + } + rawType = client.extToMime(rawExt) + mimeType = rawType + if innerExt != rawExt { + mimeType = client.extToMime(innerExt) + } + log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType) + return mimeType, rawType +} + func shouldRespBeSavedToCache(resp *http.Response) bool { if resp == nil { return false diff --git a/server/upstream/header.go b/server/upstream/header.go index 7b85df1..3a218a1 100644 --- a/server/upstream/header.go +++ b/server/upstream/header.go @@ -24,5 +24,8 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) { } else { ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) } + if encoding := header.Get(gitea.ContentEncodingHeader); encoding != "" && encoding != "identity" { + ctx.RespWriter.Header().Set(gitea.ContentEncodingHeader, encoding) + } ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat)) } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index d9c131e..98137ba 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -1,10 +1,13 @@ package upstream import ( + "cmp" "errors" "fmt" "io" "net/http" + "slices" + "strconv" "strings" "time" @@ -19,6 +22,8 @@ import ( const ( headerLastModified = "Last-Modified" headerIfModifiedSince = "If-Modified-Since" + headerAcceptEncoding = "Accept-Encoding" + headerContentEncoding = "Content-Encoding" rawMime = "text/plain; charset=utf-8" ) @@ -52,6 +57,72 @@ type Options struct { ServeRaw bool } +// allowed encodings +var allowedEncodings = map[string]string{ + "gzip": ".gz", + "br": ".br", + "zstd": ".zst", + "identity": "", +} + +// parses Accept-Encoding header into a list of acceptable encodings +func AcceptEncodings(header string) []string { + log.Trace().Msgf("got accept-encoding: %s", header) + encodings := []string{} + globQuality := 0.0 + qualities := make(map[string]float64) + + for _, encoding := range strings.Split(header, ",") { + name, quality_str, has_quality := strings.Cut(encoding, ";q=") + quality := 1.0 + + if has_quality { + var err error + quality, err = strconv.ParseFloat(quality_str, 64) + if err != nil || quality < 0 { + continue + } + } + + name = strings.TrimSpace(name) + + if name == "*" { + globQuality = quality + } else { + _, allowed := allowedEncodings[name] + if allowed { + qualities[name] = quality + if quality > 0 { + encodings = append(encodings, name) + } + } + } + } + + if globQuality > 0 { + for encoding := range allowedEncodings { + _, exists := qualities[encoding] + if !exists { + encodings = append(encodings, encoding) + qualities[encoding] = globQuality + } + } + } else { + _, exists := qualities["identity"] + if !exists { + encodings = append(encodings, "identity") + qualities["identity"] = -1 + } + } + + slices.SortStableFunc(encodings, func(x, y string) int { + // sort in reverse order; big quality comes first + return cmp.Compare(qualities[y], qualities[x]) + }) + log.Trace().Msgf("decided encoding order: %v", encodings) + return encodings +} + // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) bool { log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() @@ -97,9 +168,33 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi log.Debug().Msg("Preparing") - reader, header, statusCode, err := giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath) - if reader != nil { - defer reader.Close() + var reader io.ReadCloser + var header http.Header + var statusCode int + var err error + + // pick first non-404 response for encoding, *only* if not root + if o.TargetPath == "" || strings.HasSuffix(o.TargetPath, "/") { + err = gitea.ErrorNotFound + } else { + for _, encoding := range AcceptEncodings(ctx.Req.Header.Get(headerAcceptEncoding)) { + log.Trace().Msgf("try %s encoding", encoding) + + // add extension for encoding + path := o.TargetPath + allowedEncodings[encoding] + reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) + if statusCode == 404 { + continue + } + log.Debug().Msgf("using %s encoding", encoding) + if encoding != "identity" { + header.Set(headerContentEncoding, encoding) + } + break + } + if reader != nil { + defer reader.Close() + } } log.Debug().Msg("Aquisting") From 2f8bd38f5940be07024e679249a85e0a4f9e8e27 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Sep 2024 21:17:15 +0000 Subject: [PATCH 277/320] pin golang-just image (#390) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/390 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 12 ++++-------- flake.lock | 16 +++++++--------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 976e923..0da09a8 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -11,8 +11,7 @@ steps: build: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just - pull: true + image: codeberg.org/6543/docker-images/golang_just:go-1.22 commands: - go version - just build @@ -33,8 +32,7 @@ steps: build-tag: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just - pull: true + image: codeberg.org/6543/docker-images/golang_just:go-1.22 commands: - go version - just build-tag ${CI_COMMIT_TAG##v} @@ -43,8 +41,7 @@ steps: test: depends_on: build - image: codeberg.org/6543/docker-images/golang_just - pull: true + image: codeberg.org/6543/docker-images/golang_just:go-1.22 commands: - just test when: @@ -52,8 +49,7 @@ steps: integration-tests: depends_on: build - image: codeberg.org/6543/docker-images/golang_just - pull: true + image: codeberg.org/6543/docker-images/golang_just:go-1.22 commands: - just integration environment: diff --git a/flake.lock b/flake.lock index c74fe33..90d377f 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -19,12 +19,10 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716715802, - "narHash": "sha256-usk0vE7VlxPX8jOavrtpOqphdfqEQpf9lgedlY/r66c=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e2dd4e18cc1c7314e24154331bae07df76eb582f", - "type": "github" + "lastModified": 0, + "narHash": "sha256-8fnFlXBgM/uSvBlLWjZ0Z0sOdRBesyNdH0+esxqizGc=", + "path": "/nix/store/rqicl40jqw5d93l43s1xq0bm6yy0gpx4-source", + "type": "path" }, "original": { "id": "nixpkgs", From d6f0ec39fcdf5ce1e26308a706d859ba04cb17f1 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 30 Sep 2024 00:26:29 +0000 Subject: [PATCH 278/320] fix(deps): update golang.org/x/exp digest to 0cdaa3a (#377) --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 479d6a5..57989bf 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module codeberg.org/codeberg/pages -go 1.21 +go 1.22.0 -toolchain go1.21.4 +toolchain go1.23.1 require ( code.gitea.io/sdk/gitea v0.17.1 @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 9d9c871..a18667f 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -810,8 +810,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -868,8 +868,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1004,8 +1004,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 831ce3d913015e856351dc4d3fc983ada826ef7e Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 7 Oct 2024 00:38:22 +0000 Subject: [PATCH 279/320] fix(deps): update golang.org/x/exp digest to 225e2ab (#391) --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 57989bf..9ab2629 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 + golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index a18667f..59f2529 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1004,8 +1004,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 168bc9047b52efcbbbe0cdda95586ce9ac211d54 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 15 Oct 2024 00:51:32 +0000 Subject: [PATCH 280/320] fix(deps): update golang.org/x/exp digest to f66d83c (#396) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9ab2629..27dd9e6 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 + golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c xorm.io/xorm v1.3.2 ) diff --git a/go.sum b/go.sum index 59f2529..dab56c6 100644 --- a/go.sum +++ b/go.sum @@ -786,8 +786,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= -golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= +golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From bb7cfbb37ceb9632e2325e3f6fcf592b4833f310 Mon Sep 17 00:00:00 2001 From: Julien Malka Date: Tue, 29 Oct 2024 17:56:00 +0000 Subject: [PATCH 281/320] feat: add proxy protocol support (#394) This can be useful when the pages-server is running behing a proxy, to keep track of the originating ip. Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/394 Reviewed-by: Gusted Co-authored-by: Julien Malka Co-committed-by: Julien Malka --- cli/flags.go | 7 +++++++ config/config.go | 1 + config/setup.go | 4 ++++ go.mod | 7 ++++--- go.sum | 9 +++++++++ server/startup.go | 4 ++++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/cli/flags.go b/cli/flags.go index 934ef8d..542b6ca 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -109,6 +109,13 @@ var ( EnvVars: []string{"ENABLE_HTTP_SERVER"}, Value: false, }, + &cli.BoolFlag{ + Name: "use-proxy-protocol", + Usage: "use the proxy protocol", + EnvVars: []string{"USE_PROXY_PROTOCOL"}, + Value: false, + }, + // Default branches to fetch assets from &cli.StringSliceFlag{ Name: "pages-branch", diff --git a/config/config.go b/config/config.go index 2accbf5..8db9336 100644 --- a/config/config.go +++ b/config/config.go @@ -13,6 +13,7 @@ type ServerConfig struct { Port uint16 `default:"443"` HttpPort uint16 `default:"80"` HttpServerEnabled bool `default:"true"` + UseProxyProtocol bool `default:"false"` MainDomain string RawDomain string PagesBranches []string diff --git a/config/setup.go b/config/setup.go index f1388fe..0846314 100644 --- a/config/setup.go +++ b/config/setup.go @@ -69,6 +69,10 @@ func mergeServerConfig(ctx *cli.Context, config *ServerConfig) { if ctx.IsSet("enable-http-server") { config.HttpServerEnabled = ctx.Bool("enable-http-server") } + if ctx.IsSet("use-proxy-protocol") { + config.UseProxyProtocol = ctx.Bool("use-proxy-protocol") + } + if ctx.IsSet("pages-domain") { config.MainDomain = ctx.String("pages-domain") } diff --git a/go.mod b/go.mod index 27dd9e6..5e42e1b 100644 --- a/go.mod +++ b/go.mod @@ -104,6 +104,7 @@ require ( github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect github.com/ovh/go-ovh v1.1.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pires/go-proxyproto v0.8.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.3.0 // indirect @@ -123,10 +124,10 @@ require ( github.com/vultr/govultr/v2 v2.7.1 // indirect go.opencensus.io v0.22.3 // indirect go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect google.golang.org/api v0.20.0 // indirect diff --git a/go.sum b/go.sum index dab56c6..6903528 100644 --- a/go.sum +++ b/go.sum @@ -578,6 +578,8 @@ github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdU github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0= +github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -776,6 +778,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -852,6 +856,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -932,6 +938,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -939,6 +947,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/server/startup.go b/server/startup.go index 6642d83..14c37aa 100644 --- a/server/startup.go +++ b/server/startup.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/pires/go-proxyproto" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/urfave/cli/v2" @@ -95,6 +96,9 @@ func Serve(ctx *cli.Context) error { return fmt.Errorf("couldn't create listener: %v", err) } + if cfg.Server.UseProxyProtocol { + listener = &proxyproto.Listener{Listener: listener} + } // Setup listener for SSL connections listener = tls.NewListener(listener, certificates.TLSConfig( cfg.Server.MainDomain, From 532e1af2fcc1b2686eba0be65d4f13bf3531828b Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 5 Nov 2024 18:52:45 +0000 Subject: [PATCH 282/320] chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5 (#392) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-docker-buildx](https://codeberg.org/woodpecker-plugins/docker-buildx) ([source](https://codeberg.org/woodpecker-plugins/docker-buildx.git)) | major | `4.2.0` -> `5.0.0` | --- ### Release Notes
woodpecker-plugins/docker-buildx (woodpeckerci/plugin-docker-buildx) ### [`v5.0.0`](https://codeberg.org/woodpecker-plugins/docker-buildx/blob/HEAD/CHANGELOG.md#500---2024-10-09) [Compare Source](https://codeberg.org/woodpecker-plugins/docker-buildx/compare/v4.2.0...v5.0.0) ##### ❤️ Thanks to all contributors! ❤️ [@​6543](https://github.com/6543), [@​flyingscorpio](https://github.com/flyingscorpio), [@​pat-s](https://github.com/pat-s), [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 💥 Breaking changes - Hide insecure configuration options behind insecure tag \[[#​194](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/194)] - chore(deps): update docker docker tag to v27 \[[#​170](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/170)] ##### ✨ Features - Add `sbom` option \[[#​187](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/187)] ##### 📈 Enhancement - Optimize container layers \[[#​191](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/191)] - Dont let proxy config overwrite dedicated buildkit_driveropt proxy options if set \[[#​175](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/175)] ##### 🐛 Bug Fixes - Quote `no_proxy` \[[#​185](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/185)] - Update drone-plugin-lib to handle nil pointer \[[#​178](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/178)] ##### 📚 Documentation - update pipeline config example \[[#​180](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/180)] ##### 📦️ Dependency - chore(deps): update docker docker tag to v27.2.1 \[[#​183](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/183)] - fix(deps): update github.com/urfave/cli/v3 digest to [`31c5c84`](https://github.com/woodpecker-plugins/docker-buildx/commit/31c5c84) \[[#​182](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/182)] - Update to github.com/urfave/cli/v3 \[[#​173](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/173)] ##### Misc - dont ignore changelog ([f3f8166](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/f3f8166af3c420310ddd1dca69158a6bc70cb288)) - Add nix flake \[[#​192](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/192)] - chore(deps): update dependency go to v1.23.2 ([9c4cea3](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/9c4cea3cccce7b3b668ed5a078d71e4e0b652ec4)) - fix(deps): update github.com/urfave/cli/v3 digest to [`20ef97b`](https://github.com/woodpecker-plugins/docker-buildx/commit/20ef97b) ([b6a6f1c](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/b6a6f1c7728729882ce616a78aeb7bdda2de19be)) - Add release config for release-helper plugin \[[#​190](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/190)] - Use ready-release-go plugin \[[#​189](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/189)] - add-remote-builders \[[#​179](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/179)] - chore(deps): update docker docker tag to v27.3.1 ([4fedea4](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/4fedea48552f8e7584abb39aa50a9e8e4d1aaea9)) - chore(deps): update docker/buildx-bin docker tag to v0.17.1 ([b807eda](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/b807edaa48ff0aadd5f5e154c40ba74956e2e2a9)) - chore(deps): update davidanson/markdownlint-cli2 docker tag to v0.14.0 ([8435819](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/84358199716d7e6b4529e454772dccf50ca5e067)) - chore(deps): update dependency go to v1.23.1 ([3ee6b8b](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/3ee6b8bb9dd1170434f48b7f9d0a389e1c85a569)) - chore(deps): update docker docker tag to v27.2.0 ([c661b7a](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/c661b7a48d39b4329944f435727c06af9336a6ee)) - fix(deps): update github.com/urfave/cli/v3 digest to [`3d76e1b`](https://github.com/woodpecker-plugins/docker-buildx/commit/3d76e1b) ([ba44638](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/ba44638ddaba4aeff7fe9d59caa0f73e47f20a70)) - fix(deps): update module github.com/pelletier/go-toml/v2 to v2.2.3 ([0e1e881](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/0e1e8818e72d6fae335635be701fa43e6c10ae6b)) - fix(deps): update github.com/urfave/cli/v3 digest to [`5386081`](https://github.com/woodpecker-plugins/docker-buildx/commit/5386081) ([cf7d19c](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/cf7d19cbb1b1f94ce0d4cf868dde9d75c8b2aab5)) - fix(deps): update module honnef.co/go/tools to v0.5.1 ([23b0f61](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/23b0f6125596bdf4877262676e59490f3ac577dd)) - chore(deps): update golang docker tag to v1.23 ([b98307b](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/b98307bb3e495969737aba7dc861838720de3c21)) - fix(deps): update github.com/urfave/cli/v3 digest to [`3110c0e`](https://github.com/woodpecker-plugins/docker-buildx/commit/3110c0e) ([13aa887](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/13aa8879a21410c5ac9a83515aa9a19ca75243cb)) - chore(deps): update docker docker tag to v27.1.2 ([fe28f12](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/fe28f12be182c261560c10e6b21c487e6beaee2f)) - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.1 ([ea32257](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/ea32257d1d634daf2132dae551473cd7d473df07)) - fix(deps): update module github.com/aws/aws-sdk-go to v1.55.5 ([af71dcd](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/af71dcdbdef4a03cda1e6ac4779d75a0263293f8)) - fix(deps): update module github.com/aws/aws-sdk-go to v1.55.3 ([1d5b8fd](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/1d5b8fdc17a39cb5469c05b392cedccf06437bd3)) - chore(deps): update docker/buildx-bin docker tag to v0.16.2 ([c671088](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/c671088766b7cb467c3cb10f0d7b1069c59e853a)) - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v4.2.0 ([642f2b3](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/642f2b36d7736b0f875c985228a41ea4a8419078)) - chore(deps): update docker/buildx-bin docker tag to v0.16.1 ([f8b58f5](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/f8b58f573b2b56b53466c4e7871032244adbc8ca)) - fix(deps): update github.com/urfave/cli/v3 digest to [`127cf54`](https://github.com/woodpecker-plugins/docker-buildx/commit/127cf54) ([03cd73f](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/03cd73f06864bc0a26c66aaa2e66b07b0e9c81c5)) - update version parse lib ([42395c5](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/42395c51eca5407325619b38a90669d480ca4ef9))
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/392 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 0da09a8..10489fb 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -20,7 +20,7 @@ steps: docker-dryrun: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.2.0 + image: woodpeckerci/plugin-docker-buildx:5.0.0 settings: dockerfile: Dockerfile platforms: linux/amd64 @@ -74,7 +74,7 @@ steps: docker-next: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.2.0 + image: woodpeckerci/plugin-docker-buildx:5.0.0 settings: registry: codeberg.org dockerfile: Dockerfile @@ -89,7 +89,7 @@ steps: - event: [push] 'Publish PR image': - image: woodpeckerci/plugin-docker-buildx:4.2.0 + image: woodpeckerci/plugin-docker-buildx:5.0.0 depends_on: test settings: registry: codeberg.org @@ -107,7 +107,7 @@ steps: docker-release: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:4.2.0 + image: woodpeckerci/plugin-docker-buildx:5.0.0 settings: registry: codeberg.org dockerfile: Dockerfile From bef866faae2b699dbbb4cfc3ec18adcbaed27b8b Mon Sep 17 00:00:00 2001 From: Bastian Greshake Tzovaras Date: Tue, 5 Nov 2024 21:01:13 +0000 Subject: [PATCH 283/320] Improve error message when pages don't work (#398) On Matrix there was a recent discussion about the quality of the error page that _Codeberg Pages_ displays. Pixelcode had posted this relevant points: > Currently, the message reads as if written for the website's owner. I think it should be rephrased so that visitors who encounter the error understand that: > 1. This page is an error message, not the actual website. (e.g. Error 424 instead of just 424) > 2. The website is hosted on Codeberg Pages, not on a server controlled by the owner. > 3. There's perhaps a configuration error or an internal server issue. > 4. It might take a little until the website is up again. In particular the point about the "dual audience" of _Codeberg Pages_ user/website owner and website visitor seems important, to make sure that both audiences understand that there's an error. Also the error can be due to _Codeberg Pages_ being down or due to the user misconfiguring their page. I've taken a first stab at trying to cover those 4 points, but of course the exact improvements are up for debate, but thought it would be easiest to provide a concrete suggestion to get us started! Co-authored-by: crapStone Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/398 Co-authored-by: Bastian Greshake Tzovaras Co-committed-by: Bastian Greshake Tzovaras --- html/templates/error.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/html/templates/error.html b/html/templates/error.html index 05a5d46..ccaa682 100644 --- a/html/templates/error.html +++ b/html/templates/error.html @@ -33,12 +33,17 @@ d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z" /> -

{{.StatusText}} ({{.StatusCode}})!

+

{{.StatusText}} (Error {{.StatusCode}})!

-

Sorry, but this page couldn't be served.

+

Sorry, but this page couldn't be served:

"{{.Message}}"

- We hope this isn't a problem on our end ;) - Make sure to check the + The page you tried to reach is hosted on Codeberg Pages, which might currently be experiencing technical + difficulties. If that is the case, it could take a little while until this page is available again. +

+

+ Otherwise, this page might also be unavailable due to a configuration error. If you are the owner of this + website, please make sure to check the troubleshooting section in the Docs! From 23a8e83e80e72b11d99145cc2dfdac617ec77f14 Mon Sep 17 00:00:00 2001 From: Gnarwhal Date: Sun, 17 Nov 2024 16:28:52 +0000 Subject: [PATCH 284/320] Allow serving custom error page (#393) It might be useful for those self-hosting a Codeberg Pages instance to be able to serve a different error page than the one embedded with go:embed Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/393 Co-authored-by: Gnarwhal Co-committed-by: Gnarwhal --- README.md | 9 +++++++++ flake.lock | 10 +++++----- html/html.go | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0051a9e..4bcd393 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,15 @@ This will trigger a build of the PR which will build a docker image to be used f - `PAGES_DOMAIN` (default: `codeberg.page`): main domain for pages. - `RAW_DOMAIN` (default: `raw.codeberg.page`): domain for raw resources (must be subdomain of `PAGES_DOMAIN`). +### Custom Error Page + +A custom error page template can be served by creating `custom/error.html`. +Data available to the template includes: + +- `{{ .StatusCode }}`: The HTTP status code (e.g. 404) +- `{{ .StatusText }}`: The textual name associated with the status code (e.g. Not Found) +- `{{ .Message }}`: The reason for the error + ## Contributing to the development The Codeberg team is very open to your contribution. diff --git a/flake.lock b/flake.lock index 90d377f..861cc42 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-8fnFlXBgM/uSvBlLWjZ0Z0sOdRBesyNdH0+esxqizGc=", - "path": "/nix/store/rqicl40jqw5d93l43s1xq0bm6yy0gpx4-source", + "narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=", + "path": "/nix/store/2w8kz6zh3aq80f1dypiin222fry1rv51-source", "type": "path" }, "original": { diff --git a/html/html.go b/html/html.go index ff8b6b4..ffede7f 100644 --- a/html/html.go +++ b/html/html.go @@ -3,6 +3,8 @@ package html import ( _ "embed" "net/http" + "os" + "path" "text/template" // do not use html/template here, we sanitize the message before passing it to the template "codeberg.org/codeberg/pages/server/context" @@ -14,7 +16,7 @@ import ( var errorPage string var ( - errorTemplate = template.Must(template.New("error").Parse(errorPage)) + errorTemplate = template.Must(template.New("error").Parse(loadCustomTemplateOrDefault())) sanitizer = createBlueMondayPolicy() ) @@ -51,3 +53,19 @@ func createBlueMondayPolicy() *bluemonday.Policy { return p } + +func loadCustomTemplateOrDefault() string { + contents, err := os.ReadFile("custom/error.html") + if err != nil { + if !os.IsNotExist(err) { + wd, wdErr := os.Getwd() + if wdErr != nil { + log.Err(err).Msg("could not load custom error page 'custom/error.html'") + } else { + log.Err(err).Msgf("could not load custom error page '%v'", path.Join(wd, "custom/error.html")) + } + } + return errorPage + } + return string(contents) +} From 85059aa46ef0b04c76646c8a56a7860f5007f58a Mon Sep 17 00:00:00 2001 From: crapStone Date: Thu, 21 Nov 2024 23:26:30 +0000 Subject: [PATCH 285/320] cache pages (#403) taken from https://codeberg.org/Codeberg/pages-server/pulls/301 Co-authored-by: Moritz Marquardt Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/403 Co-authored-by: crapStone Co-committed-by: crapStone --- server/gitea/cache.go | 61 +++++++------ server/gitea/client.go | 171 ++++++++++++++++++------------------ server/upstream/header.go | 3 - server/upstream/upstream.go | 2 +- 4 files changed, 120 insertions(+), 117 deletions(-) diff --git a/server/gitea/cache.go b/server/gitea/cache.go index cfb7c2a..c560a19 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -2,6 +2,7 @@ package gitea import ( "bytes" + "encoding/json" "fmt" "io" "net/http" @@ -34,23 +35,18 @@ const ( ) type FileResponse struct { - Exists bool - IsSymlink bool - ETag string - - // uncompressed MIME type - MimeType string - - // raw MIME type (if compressed, type of compression) - RawMime string - Body []byte + Exists bool `json:"exists"` + IsSymlink bool `json:"isSymlink"` + ETag string `json:"eTag"` + MimeType string `json:"mimeType"` + Body []byte `json:"-"` // saved separately } func (f FileResponse) IsEmpty() bool { return len(f.Body) == 0 } -func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (header http.Header, statusCode int) { +func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) { header = make(http.Header) if f.Exists { @@ -63,12 +59,7 @@ func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (head header.Set(giteaObjectTypeHeader, objTypeSymlink) } header.Set(ETagHeader, f.ETag) - - if decompress { - header.Set(ContentTypeHeader, f.MimeType) - } else { - header.Set(ContentTypeHeader, f.RawMime) - } + header.Set(ContentTypeHeader, f.MimeType) header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body))) header.Set(PagesCacheIndicatorHeader, "true") @@ -77,9 +68,9 @@ func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (head } type BranchTimestamp struct { - Branch string - Timestamp time.Time - notFound bool + NotFound bool `json:"notFound"` + Branch string `json:"branch,omitempty"` + Timestamp time.Time `json:"timestamp,omitempty"` } type writeCacheReader struct { @@ -89,32 +80,46 @@ type writeCacheReader struct { cacheKey string cache cache.ICache hasError bool + doNotCache bool + complete bool } func (t *writeCacheReader) Read(p []byte) (n int, err error) { log.Trace().Msgf("[cache] read %q", t.cacheKey) n, err = t.originalReader.Read(p) + if err == io.EOF { + t.complete = true + } if err != nil && err != io.EOF { log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) t.hasError = true } else if n > 0 { - _, _ = t.buffer.Write(p[:n]) + if t.buffer.Len()+n > int(fileCacheSizeLimit) { + t.doNotCache = true + t.buffer.Reset() + } else { + _, _ = t.buffer.Write(p[:n]) + } } return } func (t *writeCacheReader) Close() error { - doWrite := !t.hasError + doWrite := !t.hasError && !t.doNotCache && t.complete fc := *t.fileResponse fc.Body = t.buffer.Bytes() - if fc.IsEmpty() { - log.Trace().Msg("[cache] file response is empty") - doWrite = false - } if doWrite { - err := t.cache.Set(t.cacheKey, fc, fileCacheTimeout) + jsonToCache, err := json.Marshal(fc) if err != nil { - log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey) + log.Trace().Err(err).Msgf("[cache] marshaling json for %q has returned an error", t.cacheKey+"|Metadata") + } + err = t.cache.Set(t.cacheKey+"|Metadata", jsonToCache, fileCacheTimeout) + if err != nil { + log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Metadata") + } + err = t.cache.Set(t.cacheKey+"|Body", fc.Body, fileCacheTimeout) + if err != nil { + log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body") } } log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite) diff --git a/server/gitea/client.go b/server/gitea/client.go index 9777043..ea57c14 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -2,6 +2,7 @@ package gitea import ( "bytes" + "encoding/json" "errors" "fmt" "io" @@ -39,10 +40,9 @@ const ( objTypeSymlink = "symlink" // std - ETagHeader = "ETag" - ContentTypeHeader = "Content-Type" - ContentLengthHeader = "Content-Length" - ContentEncodingHeader = "Content-Encoding" + ETagHeader = "ETag" + ContentTypeHeader = "Content-Type" + ContentLengthHeader = "Content-Length" ) type Client struct { @@ -104,7 +104,7 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false) + reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) if err != nil { return nil, err } @@ -112,27 +112,42 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str return io.ReadAll(reader) } -func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { +func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) { cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) log := log.With().Str("cache_key", cacheKey).Logger() log.Trace().Msg("try file in cache") // handle if cache entry exist - if cache, ok := client.responseCache.Get(cacheKey); ok { - cache := cache.(FileResponse) - cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress) - // TODO: check against some timestamp mismatch?!? + if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok { + var cache FileResponse + err := json.Unmarshal(cacheMetadata.([]byte), &cache) + if err != nil { + log.Error().Err(err).Msgf("[cache] failed to unmarshal metadata for: %s", cacheKey) + return nil, nil, http.StatusNotFound, err + } + + if !cache.Exists { + return nil, nil, http.StatusNotFound, ErrorNotFound + } + + body, ok := client.responseCache.Get(cacheKey + "|Body") + if !ok { + log.Error().Msgf("[cache] failed to get body for: %s", cacheKey) + return nil, nil, http.StatusNotFound, ErrorNotFound + } + cache.Body = body.([]byte) + + cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) if cache.Exists { - log.Debug().Msg("[cache] exists") if cache.IsSymlink { linkDest := string(cache.Body) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) - } else if !cache.IsEmpty() { + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + } else { log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil - } else if cache.IsEmpty() { - log.Debug().Msg("[cache] is empty") } + } else { + return nil, nil, http.StatusNotFound, ErrorNotFound } } log.Trace().Msg("file not in cache") @@ -166,41 +181,40 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str ETag: resp.Header.Get(ETagHeader), } log.Trace().Msgf("file response has %d bytes", len(fileResponse.Body)) - if err := client.responseCache.Set(cacheKey, fileResponse, fileCacheTimeout); err != nil { + jsonToCache, err := json.Marshal(fileResponse) + if err != nil { + log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey) + } + if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil { + log.Error().Err(err).Msg("[cache] error on cache write") + } + if err := client.responseCache.Set(cacheKey+"|Body", fileResponse.Body, fileCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) } } // now we are sure it's content so set the MIME type - mimeType, rawType := client.getMimeTypeByExtension(resource) - if decompress { - resp.Response.Header.Set(ContentTypeHeader, mimeType) - } else { - resp.Response.Header.Set(ContentTypeHeader, rawType) - } - - if !shouldRespBeSavedToCache(resp.Response) { - return reader, resp.Response.Header, resp.StatusCode, err - } + mimeType := client.getMimeTypeByExtension(resource) + resp.Response.Header.Set(ContentTypeHeader, mimeType) // now we write to cache and respond at the same time fileResp := FileResponse{ Exists: true, ETag: resp.Header.Get(ETagHeader), MimeType: mimeType, - RawMime: rawType, } return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil case http.StatusNotFound: - if err := client.responseCache.Set(cacheKey, FileResponse{ - Exists: false, - ETag: resp.Header.Get(ETagHeader), - }, fileCacheTimeout); err != nil { + jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)}) + if err != nil { + log.Error().Err(err).Msgf("[cache] marshaling json metadata for %q has returned an error", cacheKey) + } + if err := client.responseCache.Set(cacheKey+"|Metadata", jsonToCache, fileCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } @@ -215,21 +229,36 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchName string) (*BranchTimestamp, error) { cacheKey := fmt.Sprintf("%s/%s/%s/%s", branchTimestampCacheKeyPrefix, repoOwner, repoName, branchName) - if stamp, ok := client.responseCache.Get(cacheKey); ok && stamp != nil { - branchTimeStamp := stamp.(*BranchTimestamp) - if branchTimeStamp.notFound { - log.Trace().Msgf("[cache] use branch %q not found", branchName) + if stampRaw, ok := client.responseCache.Get(cacheKey); ok { + var stamp BranchTimestamp + err := json.Unmarshal(stampRaw.([]byte), &stamp) + if err != nil { + log.Error().Err(err).Bytes("stamp", stampRaw.([]byte)).Msgf("[cache] failed to unmarshal timestamp for: %s", cacheKey) return &BranchTimestamp{}, ErrorNotFound } - log.Trace().Msgf("[cache] use branch %q exist", branchName) - return branchTimeStamp, nil + + if stamp.NotFound { + log.Trace().Msgf("[cache] branch %q does not exist", branchName) + + return &BranchTimestamp{}, ErrorNotFound + } else { + log.Trace().Msgf("[cache] use branch %q exist", branchName) + // This comes from the refactoring of the caching library. + // The branch as reported by the API was stored in the cache, and I'm not sure if there are + // situations where it differs from the name in the request, hence this is left here. + return &stamp, nil + } } branch, resp, err := client.sdkClient.GetRepoBranch(repoOwner, repoName, branchName) if err != nil { if resp != nil && resp.StatusCode == http.StatusNotFound { log.Trace().Msgf("[cache] set cache branch %q not found", branchName) - if err := client.responseCache.Set(cacheKey, &BranchTimestamp{Branch: branchName, notFound: true}, branchExistenceCacheTimeout); err != nil { + jsonToCache, err := json.Marshal(BranchTimestamp{NotFound: true}) + if err != nil { + log.Error().Err(err).Msgf("[cache] marshaling empty timestamp for '%s' has returned an error", cacheKey) + } + if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return &BranchTimestamp{}, ErrorNotFound @@ -246,7 +275,11 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam } log.Trace().Msgf("set cache branch [%s] exist", branchName) - if err := client.responseCache.Set(cacheKey, stamp, branchExistenceCacheTimeout); err != nil { + jsonToCache, err := json.Marshal(stamp) + if err != nil { + log.Error().Err(err).Msgf("[cache] marshaling timestamp for %q has returned an error", cacheKey) + } + if err := client.responseCache.Set(cacheKey, jsonToCache, branchExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return stamp, nil @@ -255,8 +288,8 @@ func (client *Client) GiteaGetRepoBranchTimestamp(repoOwner, repoName, branchNam func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (string, error) { cacheKey := fmt.Sprintf("%s/%s/%s", defaultBranchCacheKeyPrefix, repoOwner, repoName) - if branch, ok := client.responseCache.Get(cacheKey); ok && branch != nil { - return branch.(string), nil + if branch, ok := client.responseCache.Get(cacheKey); ok { + return string(branch.([]byte)), nil } repo, resp, err := client.sdkClient.GetRepo(repoOwner, repoName) @@ -268,7 +301,7 @@ func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (str } branch := repo.DefaultBranch - if err := client.responseCache.Set(cacheKey, branch, defaultBranchCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, []byte(branch), defaultBranchCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return branch, nil @@ -277,13 +310,14 @@ func (client *Client) GiteaGetRepoDefaultBranch(repoOwner, repoName string) (str func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) { cacheKey := fmt.Sprintf("%s/%s", ownerExistenceKeyPrefix, owner) - if exist, ok := client.responseCache.Get(cacheKey); ok && exist != nil { - return exist.(bool), nil + if existRaw, ok := client.responseCache.Get(cacheKey); ok && existRaw != nil { + exist, err := strconv.ParseBool(existRaw.(string)) + return exist, err } _, resp, err := client.sdkClient.GetUserInfo(owner) if resp.StatusCode == http.StatusOK && err == nil { - if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return true, nil @@ -293,59 +327,26 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) { _, resp, err = client.sdkClient.GetOrg(owner) if resp.StatusCode == http.StatusOK && err == nil { - if err := client.responseCache.Set(cacheKey, true, ownerExistenceCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, []byte("true"), ownerExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return true, nil } else if resp.StatusCode != http.StatusNotFound { return false, err } - if err := client.responseCache.Set(cacheKey, false, ownerExistenceCacheTimeout); err != nil { + if err := client.responseCache.Set(cacheKey, []byte("false"), ownerExistenceCacheTimeout); err != nil { log.Error().Err(err).Msg("[cache] error on cache write") } return false, nil } -func (client *Client) extToMime(ext string) string { - mimeType := mime.TypeByExtension(ext) +func (client *Client) getMimeTypeByExtension(resource string) string { + mimeType := mime.TypeByExtension(path.Ext(resource)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { mimeType = client.defaultMimeType } + log.Trace().Msgf("probe mime of %q is %q", resource, mimeType) + return mimeType } - -func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) { - rawExt := path.Ext(resource) - innerExt := rawExt - switch rawExt { - case ".gz", ".br", ".zst": - innerExt = path.Ext(resource[:len(resource)-len(rawExt)]) - } - rawType = client.extToMime(rawExt) - mimeType = rawType - if innerExt != rawExt { - mimeType = client.extToMime(innerExt) - } - log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType) - return mimeType, rawType -} - -func shouldRespBeSavedToCache(resp *http.Response) bool { - if resp == nil { - return false - } - - contentLengthRaw := resp.Header.Get(ContentLengthHeader) - if contentLengthRaw == "" { - return false - } - - contentLength, err := strconv.ParseInt(contentLengthRaw, 10, 64) - if err != nil { - log.Error().Err(err).Msg("could not parse content length") - } - - // if content to big or could not be determined we not cache it - return contentLength > 0 && contentLength < fileCacheSizeLimit -} diff --git a/server/upstream/header.go b/server/upstream/header.go index 3a218a1..7b85df1 100644 --- a/server/upstream/header.go +++ b/server/upstream/header.go @@ -24,8 +24,5 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) { } else { ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) } - if encoding := header.Get(gitea.ContentEncodingHeader); encoding != "" && encoding != "identity" { - ctx.RespWriter.Header().Set(gitea.ContentEncodingHeader, encoding) - } ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat)) } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 98137ba..2f1751b 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -182,7 +182,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // add extension for encoding path := o.TargetPath + allowedEncodings[encoding] - reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) + reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path) if statusCode == 404 { continue } From 044c684a47853af53c660e454328348a49277c9c Mon Sep 17 00:00:00 2001 From: crapStone Date: Mon, 25 Nov 2024 12:21:55 +0000 Subject: [PATCH 286/320] Fix compression (#405) closes #404 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/405 Co-authored-by: crapStone Co-committed-by: crapStone --- flake.lock | 4 ++-- flake.nix | 2 +- server/gitea/cache.go | 15 ++++++++---- server/gitea/client.go | 47 +++++++++++++++++++++++++++---------- server/upstream/header.go | 3 +++ server/upstream/upstream.go | 2 +- 6 files changed, 53 insertions(+), 20 deletions(-) diff --git a/flake.lock b/flake.lock index 861cc42..5eb7802 100644 --- a/flake.lock +++ b/flake.lock @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=", - "path": "/nix/store/2w8kz6zh3aq80f1dypiin222fry1rv51-source", + "narHash": "sha256-29QfSvJwpjNwppFnU33nnyedAWpaaDBSlDJZzJhg97s=", + "path": "/nix/store/1703v0vkmk136sca5rf1861jrn2ndajr-source", "type": "path" }, "original": { diff --git a/flake.nix b/flake.nix index 61f3b55..36a545e 100644 --- a/flake.nix +++ b/flake.nix @@ -13,7 +13,7 @@ in { devShells.default = pkgs.mkShell { buildInputs = with pkgs; [ - gcc + glibc.static go gofumpt golangci-lint diff --git a/server/gitea/cache.go b/server/gitea/cache.go index c560a19..276358c 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -38,15 +38,16 @@ type FileResponse struct { Exists bool `json:"exists"` IsSymlink bool `json:"isSymlink"` ETag string `json:"eTag"` - MimeType string `json:"mimeType"` - Body []byte `json:"-"` // saved separately + MimeType string `json:"mimeType"` // uncompressed MIME type + RawMime string `json:"rawMime"` // raw MIME type (if compressed, type of compression) + Body []byte `json:"-"` // saved separately } func (f FileResponse) IsEmpty() bool { return len(f.Body) == 0 } -func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, statusCode int) { +func (f FileResponse) createHttpResponse(cacheKey string, decompress bool) (header http.Header, statusCode int) { header = make(http.Header) if f.Exists { @@ -59,7 +60,13 @@ func (f FileResponse) createHttpResponse(cacheKey string) (header http.Header, s header.Set(giteaObjectTypeHeader, objTypeSymlink) } header.Set(ETagHeader, f.ETag) - header.Set(ContentTypeHeader, f.MimeType) + + if decompress { + header.Set(ContentTypeHeader, f.MimeType) + } else { + header.Set(ContentTypeHeader, f.RawMime) + } + header.Set(ContentLengthHeader, fmt.Sprintf("%d", len(f.Body))) header.Set(PagesCacheIndicatorHeader, "true") diff --git a/server/gitea/client.go b/server/gitea/client.go index ea57c14..5a12111 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -40,9 +40,10 @@ const ( objTypeSymlink = "symlink" // std - ETagHeader = "ETag" - ContentTypeHeader = "Content-Type" - ContentLengthHeader = "Content-Length" + ETagHeader = "ETag" + ContentTypeHeader = "Content-Type" + ContentLengthHeader = "Content-Length" + ContentEncodingHeader = "Content-Encoding" ) type Client struct { @@ -104,7 +105,7 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s } func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource) + reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false) if err != nil { return nil, err } @@ -112,7 +113,7 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str return io.ReadAll(reader) } -func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string) (io.ReadCloser, http.Header, int, error) { +func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) log := log.With().Str("cache_key", cacheKey).Logger() log.Trace().Msg("try file in cache") @@ -136,12 +137,12 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str } cache.Body = body.([]byte) - cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey) + cachedHeader, cachedStatusCode := cache.createHttpResponse(cacheKey, decompress) if cache.Exists { if cache.IsSymlink { linkDest := string(cache.Body) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) } else { log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil @@ -193,19 +194,25 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str } log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest) + return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) } } // now we are sure it's content so set the MIME type - mimeType := client.getMimeTypeByExtension(resource) + mimeType, rawType := client.getMimeTypeByExtension(resource) resp.Response.Header.Set(ContentTypeHeader, mimeType) + if decompress { + resp.Response.Header.Set(ContentTypeHeader, mimeType) + } else { + resp.Response.Header.Set(ContentTypeHeader, rawType) + } // now we write to cache and respond at the same time fileResp := FileResponse{ Exists: true, ETag: resp.Header.Get(ETagHeader), MimeType: mimeType, + RawMime: rawType, } return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil @@ -340,13 +347,29 @@ func (client *Client) GiteaCheckIfOwnerExists(owner string) (bool, error) { return false, nil } -func (client *Client) getMimeTypeByExtension(resource string) string { - mimeType := mime.TypeByExtension(path.Ext(resource)) +func (client *Client) extToMime(ext string) string { + mimeType := mime.TypeByExtension(path.Ext(ext)) mimeTypeSplit := strings.SplitN(mimeType, ";", 2) if client.forbiddenMimeTypes[mimeTypeSplit[0]] || mimeType == "" { mimeType = client.defaultMimeType } - log.Trace().Msgf("probe mime of %q is %q", resource, mimeType) + log.Trace().Msgf("probe mime of extension '%q' is '%q'", ext, mimeType) return mimeType } + +func (client *Client) getMimeTypeByExtension(resource string) (mimeType, rawType string) { + rawExt := path.Ext(resource) + innerExt := rawExt + switch rawExt { + case ".gz", ".br", ".zst": + innerExt = path.Ext(resource[:len(resource)-len(rawExt)]) + } + rawType = client.extToMime(rawExt) + mimeType = rawType + if innerExt != rawExt { + mimeType = client.extToMime(innerExt) + } + log.Trace().Msgf("probe mime of %q is (%q / raw %q)", resource, mimeType, rawType) + return mimeType, rawType +} diff --git a/server/upstream/header.go b/server/upstream/header.go index 7b85df1..3a218a1 100644 --- a/server/upstream/header.go +++ b/server/upstream/header.go @@ -24,5 +24,8 @@ func (o *Options) setHeader(ctx *context.Context, header http.Header) { } else { ctx.RespWriter.Header().Set(gitea.ContentTypeHeader, mime) } + if encoding := header.Get(gitea.ContentEncodingHeader); encoding != "" && encoding != "identity" { + ctx.RespWriter.Header().Set(gitea.ContentEncodingHeader, encoding) + } ctx.RespWriter.Header().Set(headerLastModified, o.BranchTimestamp.In(time.UTC).Format(http.TimeFormat)) } diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 2f1751b..98137ba 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -182,7 +182,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // add extension for encoding path := o.TargetPath + allowedEncodings[encoding] - reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path) + reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) if statusCode == 404 { continue } From c9f1624afd6b15c64745b778bdd7c7584af4f74b Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 25 Nov 2024 12:26:32 +0000 Subject: [PATCH 287/320] fix(deps): update golang.org/x/exp digest to 2d47ceb (#401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | golang.org/x/exp | require | digest | `f66d83c` -> `2d47ceb` | --- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/401 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- go.mod | 4 ++-- go.sum | 22 +++++++++------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 5e42e1b..4f1666f 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,12 @@ require ( github.com/mattn/go-sqlite3 v1.14.16 github.com/microcosm-cc/bluemonday v1.0.26 github.com/pelletier/go-toml/v2 v2.1.0 + github.com/pires/go-proxyproto v0.8.0 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad github.com/rs/zerolog v1.27.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f xorm.io/xorm v1.3.2 ) @@ -104,7 +105,6 @@ require ( github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect github.com/ovh/go-ovh v1.1.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect - github.com/pires/go-proxyproto v0.8.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/otp v1.3.0 // indirect diff --git a/go.sum b/go.sum index 6903528..7c4b251 100644 --- a/go.sum +++ b/go.sum @@ -776,7 +776,6 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= @@ -790,8 +789,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -814,8 +813,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -854,8 +853,6 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -874,8 +871,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -936,7 +933,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -945,9 +941,9 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1013,8 +1009,8 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From e079ce4cf40c0609feec61ae6b4dbef2bc279c97 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Mon, 25 Nov 2024 12:26:54 +0000 Subject: [PATCH 288/320] chore(deps): update golangci/golangci-lint docker tag to v1.62.0 (#402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | minor | `v1.61.0` -> `v1.62.0` | --- ### Release Notes

golangci/golangci-lint (golangci/golangci-lint) ### [`v1.62.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1620) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.61.0...v1.62.0) 1. New linters - Add `recvcheck` linter https://github.com/raeperd/recvcheck - Add `iface` linter https://github.com/uudashr/iface 2. Updated linters - ⚠️ `execinquery`: deprecation step 2 - ⚠️ `gomnd`: deprecation step 2 (replaced by `mnd`) - `bidichk`: from 0.2.7 to 0.3.2 (important performance improvement) - `canonicalheader`: from 1.1.1 to 1.1.2 - `cyclop`: from 1.2.1 to 1.2.3 - `dupword`: from 0.1.1 to 0.1.3 - `errcheck`: from 1.7.0 to 1.8.0 - `errchkjson`: from 0.3.6 to 0.4.0 - `errname`: from 0.1.13 to 1.0.0 - `ginkgolinter`: from 0.17.0 to 0.18.0 (new option: `force-succeed`) - `go-check-sumtype`: from 0.1.4 to 0.2.0 (new option: `default-signifies-exhaustive`) - `go-critic`: from 0.11.4 to 0.11.5 - `go-printf-func-name`: from [`7558a9e`](https://github.com/golangci/golangci-lint/commit/7558a9eaa5af) to v0.1.0 - `godot`: from 1.4.17 to 1.4.18 - `gosec`: from 2.21.2 to 2.21.4 - `intrange`: from 0.2.0 to 0.2.1 - `loggercheck`: from 0.9.4 to 0.10.1 (`log/slog` support) - `musttag`: from 0.12.2 to 0.13.0 - `nakedret`: from 2.0.4 to 2.0.5 - `nilnil`: from 0.1.9 to 1.0.0 (new option: `detect-opposite`) - `noctx`: from 0.0.2 to 0.1.0 - `protogetter`: from 0.3.6 to 0.3.8 - `revive`: from 1.3.9 to 1.5.0 (new rules: `filename-format`, and `file-length-limit`) - `tenv`: from 1.10.0 to 1.12.1 (handle dot import) - `testifylint`: from 1.4.3 to 1.5.0 (new checkers: `contains`, `encoded-compare`, `regexp`) 3. Misc. - Type sizing when cross-compiling (32-bit). - code-climate: add check_name field - Improve Go version detection - Fix Go version propagation 4. Documentation - Adds a section about `exclude-dirs-use-default` - Improve 'install from sources' section - Improve FAQ about Go versions - Improve linter/rule/check docs - Improve new linter section - Improve `forbidigo` pattern examples for built-in functions
--- ### Configuration 📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/402 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 3b7ca40..d8ef836 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.61.0 + image: golangci/golangci-lint:v1.62.0 commands: - go version - go install mvdan.cc/gofumpt@latest From 2438de0eb2d843a99d7e2a0fb6a71db8c9c70e2e Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Dec 2024 15:43:30 +0000 Subject: [PATCH 289/320] fix panic when upstream fails (#412) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/412 Co-authored-by: crapStone Co-committed-by: crapStone --- flake.lock | 4 ++-- server/upstream/upstream.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 5eb7802..1062fa4 100644 --- a/flake.lock +++ b/flake.lock @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-29QfSvJwpjNwppFnU33nnyedAWpaaDBSlDJZzJhg97s=", - "path": "/nix/store/1703v0vkmk136sca5rf1861jrn2ndajr-source", + "narHash": "sha256-UA56kDQSztRWbpsG9bK+jIGyZ0YZAcIsYAhXDHIr8zs=", + "path": "/nix/store/np3i3qi6hq6d1p5xr36cjmr0xwd2j289-source", "type": "path" }, "original": { diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 98137ba..2abd0b9 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -183,9 +183,12 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // add extension for encoding path := o.TargetPath + allowedEncodings[encoding] reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) - if statusCode == 404 { + if statusCode == http.StatusNotFound { continue } + if err != nil { + break + } log.Debug().Msgf("using %s encoding", encoding) if encoding != "identity" { header.Set(headerContentEncoding, encoding) From ef7e2cd7bbdaaad90347791050d9c287629caa0a Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Dec 2024 18:49:25 +0000 Subject: [PATCH 290/320] Add reqId to all logging messages I could find (#413) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/413 Co-authored-by: crapStone Co-committed-by: crapStone --- .gitignore | 1 + flake.lock | 4 ++-- go.mod | 1 + go.sum | 2 ++ server/certificates/certificates.go | 28 +++++++++++++++---------- server/context/context.go | 10 +++++++++ server/gitea/cache.go | 19 ++++++++++------- server/gitea/client.go | 15 ++++++------- server/handler/handler.go | 4 ++-- server/handler/handler_custom_domain.go | 6 +++--- server/handler/handler_raw_domain.go | 4 ++-- server/handler/handler_sub_domain.go | 10 ++++----- server/handler/try.go | 12 ++++++++--- server/startup.go | 9 ++++---- server/upstream/domains.go | 5 +++-- server/upstream/redirects.go | 4 ++-- server/upstream/upstream.go | 8 +++---- 17 files changed, 88 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 3035107..66e7084 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ vendor/ pages certs.sqlite .bash_history +pkg/ diff --git a/flake.lock b/flake.lock index 1062fa4..1fdb364 100644 --- a/flake.lock +++ b/flake.lock @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-UA56kDQSztRWbpsG9bK+jIGyZ0YZAcIsYAhXDHIr8zs=", - "path": "/nix/store/np3i3qi6hq6d1p5xr36cjmr0xwd2j289-source", + "narHash": "sha256-zUfNgGopg1+/xYRnFaI6PAhlF2JofdEG789CsKclYSw=", + "path": "/nix/store/0ijldwygklvrzinxx7scsxsvqhx0hqgz-source", "type": "path" }, "original": { diff --git a/go.mod b/go.mod index 4f1666f..1bc1de6 100644 --- a/go.mod +++ b/go.mod @@ -72,6 +72,7 @@ require ( github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect diff --git a/go.sum b/go.sum index 7c4b251..6aef8a6 100644 --- a/go.sum +++ b/go.sum @@ -317,6 +317,8 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= diff --git a/server/certificates/certificates.go b/server/certificates/certificates.go index 062f721..aeb619f 100644 --- a/server/certificates/certificates.go +++ b/server/certificates/certificates.go @@ -16,9 +16,11 @@ import ( "github.com/go-acme/lego/v4/lego" "github.com/hashicorp/golang-lru/v2/expirable" "github.com/reugn/equalizer" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" + psContext "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/database" dnsutils "codeberg.org/codeberg/pages/server/dns" "codeberg.org/codeberg/pages/server/gitea" @@ -43,7 +45,11 @@ func TLSConfig(mainDomainSuffix string, return &tls.Config{ // check DNS name & get certificate from Let's Encrypt GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + ctx := psContext.New(nil, nil) + log := log.With().Str("ReqId", ctx.ReqId).Logger() + domain := strings.ToLower(strings.TrimSpace(info.ServerName)) + log.Debug().Str("domain", domain).Msg("start: get tls certificate") if len(domain) < 1 { return nil, errors.New("missing domain info via SNI (RFC 4366, Section 3.1)") } @@ -100,7 +106,7 @@ func TLSConfig(mainDomainSuffix string, TargetRepo: targetRepo, TargetBranch: targetBranch, } - _, valid := targetOpt.CheckCanonicalDomain(giteaClient, domain, mainDomainSuffix, canonicalDomainCache) + _, valid := targetOpt.CheckCanonicalDomain(ctx, giteaClient, domain, mainDomainSuffix, canonicalDomainCache) if !valid { // We shouldn't obtain a certificate when we cannot check if the // repository has specified this domain in the `.domains` file. @@ -116,7 +122,7 @@ func TLSConfig(mainDomainSuffix string, var tlsCertificate *tls.Certificate var err error - if tlsCertificate, err = acmeClient.retrieveCertFromDB(domain, mainDomainSuffix, false, certDB); err != nil { + if tlsCertificate, err = acmeClient.retrieveCertFromDB(log, domain, mainDomainSuffix, false, certDB); err != nil { if !errors.Is(err, database.ErrNotFound) { return nil, err } @@ -130,7 +136,7 @@ func TLSConfig(mainDomainSuffix string, return nil, fmt.Errorf("won't request certificate for %q", domain) } - tlsCertificate, err = acmeClient.obtainCert(acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB) + tlsCertificate, err = acmeClient.obtainCert(log, acmeClient.legoClient, []string{domain}, nil, targetOwner, false, mainDomainSuffix, certDB) if err != nil { return nil, err } @@ -173,7 +179,7 @@ func (c *AcmeClient) checkUserLimit(user string) error { return nil } -func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) { +func (c *AcmeClient) retrieveCertFromDB(log zerolog.Logger, sni, mainDomainSuffix string, useDnsProvider bool, certDB database.CertDB) (*tls.Certificate, error) { // parse certificate from database res, err := certDB.Get(sni) if err != nil { @@ -206,7 +212,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv // TODO: make a queue ? go (func() { res.CSR = nil // acme client doesn't like CSR to be set - if _, err := c.obtainCert(c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil { + if _, err := c.obtainCert(log, c.legoClient, []string{sni}, res, "", useDnsProvider, mainDomainSuffix, certDB); err != nil { log.Error().Msgf("Couldn't renew certificate for %s: %v", sni, err) } })() @@ -216,7 +222,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv return &tlsCertificate, nil } -func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { +func (c *AcmeClient) obtainCert(log zerolog.Logger, acmeClient *lego.Client, domains []string, renew *certificate.Resource, user string, useDnsProvider bool, mainDomainSuffix string, keyDatabase database.CertDB) (*tls.Certificate, error) { name := strings.TrimPrefix(domains[0], "*") // lock to avoid simultaneous requests @@ -226,7 +232,7 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew time.Sleep(100 * time.Millisecond) _, working = c.obtainLocks.Load(name) } - cert, err := c.retrieveCertFromDB(name, mainDomainSuffix, useDnsProvider, keyDatabase) + cert, err := c.retrieveCertFromDB(log, name, mainDomainSuffix, useDnsProvider, keyDatabase) if err != nil { return nil, fmt.Errorf("certificate failed in synchronous request: %w", err) } @@ -320,7 +326,7 @@ func (c *AcmeClient) obtainCert(acmeClient *lego.Client, domains []string, renew return &tlsCertificate, nil } -func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error { +func SetupMainDomainCertificates(log zerolog.Logger, mainDomainSuffix string, acmeClient *AcmeClient, certDB database.CertDB) error { // getting main cert before ACME account so that we can fail here without hitting rate limits mainCertBytes, err := certDB.Get(mainDomainSuffix) if err != nil && !errors.Is(err, database.ErrNotFound) { @@ -328,7 +334,7 @@ func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient } if mainCertBytes == nil { - _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB) + _, err = acmeClient.obtainCert(log, acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, nil, "", true, mainDomainSuffix, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew main domain certificate, continuing with mock certs only") } @@ -337,7 +343,7 @@ func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient return nil } -func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) { +func MaintainCertDB(log zerolog.Logger, ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) { for { // delete expired certs that will be invalid until next clean up threshold := time.Now().Add(interval) @@ -375,7 +381,7 @@ func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *Acm } else if tlsCertificates[0].NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) { // renew main certificate 30 days before it expires go (func() { - _, err = acmeClient.obtainCert(acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB) + _, err = acmeClient.obtainCert(log, acmeClient.dnsChallengerLegoClient, []string{"*" + mainDomainSuffix, mainDomainSuffix[1:]}, res, "", true, mainDomainSuffix, certDB) if err != nil { log.Error().Err(err).Msg("Couldn't renew certificate for main domain") } diff --git a/server/context/context.go b/server/context/context.go index 6650164..e695ab7 100644 --- a/server/context/context.go +++ b/server/context/context.go @@ -5,19 +5,29 @@ import ( "net/http" "codeberg.org/codeberg/pages/server/utils" + "github.com/hashicorp/go-uuid" + "github.com/rs/zerolog/log" ) type Context struct { RespWriter http.ResponseWriter Req *http.Request StatusCode int + ReqId string } func New(w http.ResponseWriter, r *http.Request) *Context { + req_uuid, err := uuid.GenerateUUID() + if err != nil { + log.Error().Err(err).Msg("Failed to generate request id, assigning error value") + req_uuid = "ERROR" + } + return &Context{ RespWriter: w, Req: r, StatusCode: http.StatusOK, + ReqId: req_uuid, } } diff --git a/server/gitea/cache.go b/server/gitea/cache.go index 276358c..03f40a9 100644 --- a/server/gitea/cache.go +++ b/server/gitea/cache.go @@ -8,9 +8,11 @@ import ( "net/http" "time" + "github.com/rs/zerolog" "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" ) const ( @@ -89,16 +91,17 @@ type writeCacheReader struct { hasError bool doNotCache bool complete bool + log zerolog.Logger } func (t *writeCacheReader) Read(p []byte) (n int, err error) { - log.Trace().Msgf("[cache] read %q", t.cacheKey) + t.log.Trace().Msgf("[cache] read %q", t.cacheKey) n, err = t.originalReader.Read(p) if err == io.EOF { t.complete = true } if err != nil && err != io.EOF { - log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) + t.log.Trace().Err(err).Msgf("[cache] original reader for %q has returned an error", t.cacheKey) t.hasError = true } else if n > 0 { if t.buffer.Len()+n > int(fileCacheSizeLimit) { @@ -118,22 +121,23 @@ func (t *writeCacheReader) Close() error { if doWrite { jsonToCache, err := json.Marshal(fc) if err != nil { - log.Trace().Err(err).Msgf("[cache] marshaling json for %q has returned an error", t.cacheKey+"|Metadata") + t.log.Trace().Err(err).Msgf("[cache] marshaling json for %q has returned an error", t.cacheKey+"|Metadata") } err = t.cache.Set(t.cacheKey+"|Metadata", jsonToCache, fileCacheTimeout) if err != nil { - log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Metadata") + t.log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Metadata") } err = t.cache.Set(t.cacheKey+"|Body", fc.Body, fileCacheTimeout) if err != nil { - log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body") + t.log.Trace().Err(err).Msgf("[cache] writer for %q has returned an error", t.cacheKey+"|Body") } } - log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite) + t.log.Trace().Msgf("cacheReader for %q saved=%t closed", t.cacheKey, doWrite) return t.originalReader.Close() } -func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser { +func (f FileResponse) CreateCacheReader(ctx *context.Context, r io.ReadCloser, cache cache.ICache, cacheKey string) io.ReadCloser { + log := log.With().Str("ReqId", ctx.ReqId).Logger() if r == nil || cache == nil || cacheKey == "" { log.Error().Msg("could not create CacheReader") return nil @@ -145,5 +149,6 @@ func (f FileResponse) CreateCacheReader(r io.ReadCloser, cache cache.ICache, cac fileResponse: &f, cache: cache, cacheKey: cacheKey, + log: log, } } diff --git a/server/gitea/client.go b/server/gitea/client.go index 5a12111..0cf6e6c 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -19,6 +19,7 @@ import ( "codeberg.org/codeberg/pages/config" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/version" ) @@ -104,8 +105,8 @@ func (client *Client) ContentWebLink(targetOwner, targetRepo, branch, resource s return path.Join(client.giteaRoot, targetOwner, targetRepo, "src/branch", branch, resource) } -func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource string) ([]byte, error) { - reader, _, _, err := client.ServeRawContent(targetOwner, targetRepo, ref, resource, false) +func (client *Client) GiteaRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string) ([]byte, error) { + reader, _, _, err := client.ServeRawContent(ctx, targetOwner, targetRepo, ref, resource, false) if err != nil { return nil, err } @@ -113,9 +114,9 @@ func (client *Client) GiteaRawContent(targetOwner, targetRepo, ref, resource str return io.ReadAll(reader) } -func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { +func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetRepo, ref, resource string, decompress bool) (io.ReadCloser, http.Header, int, error) { cacheKey := fmt.Sprintf("%s/%s/%s|%s|%s", rawContentCacheKeyPrefix, targetOwner, targetRepo, ref, resource) - log := log.With().Str("cache_key", cacheKey).Logger() + log := log.With().Str("ReqId", ctx.ReqId).Str("cache_key", cacheKey).Logger() log.Trace().Msg("try file in cache") // handle if cache entry exist if cacheMetadata, ok := client.responseCache.Get(cacheKey + "|Metadata"); ok { @@ -142,7 +143,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str if cache.IsSymlink { linkDest := string(cache.Body) log.Debug().Msgf("[cache] follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) + return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress) } else { log.Debug().Msgf("[cache] return %d bytes", len(cache.Body)) return io.NopCloser(bytes.NewReader(cache.Body)), cachedHeader, cachedStatusCode, nil @@ -194,7 +195,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str } log.Debug().Msgf("follow symlink from %q to %q", resource, linkDest) - return client.ServeRawContent(targetOwner, targetRepo, ref, linkDest, decompress) + return client.ServeRawContent(ctx, targetOwner, targetRepo, ref, linkDest, decompress) } } @@ -214,7 +215,7 @@ func (client *Client) ServeRawContent(targetOwner, targetRepo, ref, resource str MimeType: mimeType, RawMime: rawType, } - return fileResp.CreateCacheReader(reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil + return fileResp.CreateCacheReader(ctx, reader, client.responseCache, cacheKey), resp.Response.Header, resp.StatusCode, nil case http.StatusNotFound: jsonToCache, err := json.Marshal(FileResponse{ETag: resp.Header.Get(ETagHeader)}) diff --git a/server/handler/handler.go b/server/handler/handler.go index c038c2d..437697a 100644 --- a/server/handler/handler.go +++ b/server/handler/handler.go @@ -26,9 +26,9 @@ func Handler( canonicalDomainCache, redirectsCache cache.ICache, ) http.HandlerFunc { return func(w http.ResponseWriter, req *http.Request) { - log.Debug().Msg("\n----------------------------------------------------------") - log := log.With().Strs("Handler", []string{req.Host, req.RequestURI}).Logger() ctx := context.New(w, req) + log := log.With().Str("ReqId", ctx.ReqId).Strs("Handler", []string{req.Host, req.RequestURI}).Logger() + log.Debug().Msg("\n----------------------------------------------------------") ctx.RespWriter.Header().Set("Server", "pages-server") diff --git a/server/handler/handler_custom_domain.go b/server/handler/handler_custom_domain.go index 852001a..8a5f9d7 100644 --- a/server/handler/handler_custom_domain.go +++ b/server/handler/handler_custom_domain.go @@ -47,7 +47,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g TargetBranch: targetBranch, TargetPath: path.Join(pathParts...), }, canonicalLink); works { - canonicalDomain, valid := targetOpt.CheckCanonicalDomain(giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) + canonicalDomain, valid := targetOpt.CheckCanonicalDomain(ctx, giteaClient, trimmedHost, mainDomainSuffix, canonicalDomainCache) if !valid { html.ReturnErrorPage(ctx, "domain not specified in .domains file", http.StatusMisdirectedRequest) return @@ -63,8 +63,8 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g return } - log.Debug().Msg("tryBranch, now trying upstream 7") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + log.Debug().Str("url", trimmedHost).Msg("tryBranch, now trying upstream") + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } diff --git a/server/handler/handler_raw_domain.go b/server/handler/handler_raw_domain.go index f48e8e4..bbbf7da 100644 --- a/server/handler/handler_raw_domain.go +++ b/server/handler/handler_raw_domain.go @@ -45,7 +45,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie TargetPath: path.Join(pathElements[3:]...), }, true); works { log.Trace().Msg("tryUpstream: serve raw domain with specified branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } log.Debug().Msg("missing branch info") @@ -62,7 +62,7 @@ func handleRaw(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Clie TargetPath: path.Join(pathElements[2:]...), }, true); works { log.Trace().Msg("tryUpstream: serve raw domain with default branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage(ctx, fmt.Sprintf("raw domain could not find repo %s/%s or repo is empty", targetOpt.TargetOwner, targetOpt.TargetRepo), diff --git a/server/handler/handler_sub_domain.go b/server/handler/handler_sub_domain.go index 806fe7f..e335019 100644 --- a/server/handler/handler_sub_domain.go +++ b/server/handler/handler_sub_domain.go @@ -53,7 +53,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[2:]...), }, true); works { log.Trace().Msg("tryUpstream: serve with specified repo and branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage( ctx, @@ -85,7 +85,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[1:]...), }, true); works { log.Trace().Msg("tryUpstream: serve default pages repo with specified branch") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) } else { html.ReturnErrorPage( ctx, @@ -110,7 +110,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements[1:]...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 5") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } } @@ -126,7 +126,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } } @@ -141,7 +141,7 @@ func handleSubDomain(log zerolog.Logger, ctx *context.Context, giteaClient *gite TargetPath: path.Join(pathElements...), }, false); works { log.Debug().Msg("tryBranch, now trying upstream 6") - tryUpstream(ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) + tryUpstream(log, ctx, giteaClient, mainDomainSuffix, trimmedHost, targetOpt, canonicalDomainCache, redirectsCache) return } diff --git a/server/handler/try.go b/server/handler/try.go index e76891d..e5fc49b 100644 --- a/server/handler/try.go +++ b/server/handler/try.go @@ -15,7 +15,7 @@ import ( ) // tryUpstream forwards the target request to the Gitea API, and shows an error page on failure. -func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, +func tryUpstream(log zerolog.Logger, ctx *context.Context, giteaClient *gitea.Client, mainDomainSuffix, trimmedHost string, options *upstream.Options, canonicalDomainCache cache.ICache, @@ -23,7 +23,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, ) { // check if a canonical domain exists on a request on MainDomain if strings.HasSuffix(trimmedHost, mainDomainSuffix) && !options.ServeRaw { - canonicalDomain, _ := options.CheckCanonicalDomain(giteaClient, "", mainDomainSuffix, canonicalDomainCache) + canonicalDomain, _ := options.CheckCanonicalDomain(ctx, giteaClient, "", mainDomainSuffix, canonicalDomainCache) if !strings.HasSuffix(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix) { canonicalPath := ctx.Req.RequestURI if options.TargetRepo != defaultPagesRepo { @@ -32,7 +32,12 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, canonicalPath = "/" + path[2] } } - ctx.Redirect("https://"+canonicalDomain+canonicalPath, http.StatusTemporaryRedirect) + + redirect_to := "https://" + canonicalDomain + canonicalPath + + log.Debug().Str("to", redirect_to).Msg("redirecting") + + ctx.Redirect(redirect_to, http.StatusTemporaryRedirect) return } } @@ -41,6 +46,7 @@ func tryUpstream(ctx *context.Context, giteaClient *gitea.Client, options.Host = trimmedHost // Try to request the file from the Gitea API + log.Debug().Msg("requesting from upstream") if !options.Upstream(ctx, giteaClient, redirectsCache) { html.ReturnErrorPage(ctx, fmt.Sprintf("Forge returned %d %s", ctx.StatusCode, http.StatusText(ctx.StatusCode)), ctx.StatusCode) } diff --git a/server/startup.go b/server/startup.go index 14c37aa..4ae26c1 100644 --- a/server/startup.go +++ b/server/startup.go @@ -27,7 +27,7 @@ import ( // Serve sets up and starts the web server. func Serve(ctx *cli.Context) error { // initialize logger with Trace, overridden later with actual level - log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(zerolog.TraceLevel) + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Caller().Logger().Level(zerolog.TraceLevel) cfg, err := config.ReadConfig(ctx) if err != nil { @@ -41,7 +41,8 @@ func Serve(ctx *cli.Context) error { if err != nil { return err } - log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger().Level(logLevel) + fmt.Printf("Setting log level to: %s\n", logLevel) + log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Caller().Logger().Level(logLevel) listeningSSLAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port) listeningHTTPAddress := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.HttpPort) @@ -85,7 +86,7 @@ func Serve(ctx *cli.Context) error { return err } - if err := certificates.SetupMainDomainCertificates(cfg.Server.MainDomain, acmeClient, certDB); err != nil { + if err := certificates.SetupMainDomainCertificates(log.Logger, cfg.Server.MainDomain, acmeClient, certDB); err != nil { return err } @@ -114,7 +115,7 @@ func Serve(ctx *cli.Context) error { interval := 12 * time.Hour certMaintainCtx, cancelCertMaintain := context.WithCancel(context.Background()) defer cancelCertMaintain() - go certificates.MaintainCertDB(certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB) + go certificates.MaintainCertDB(log.Logger, certMaintainCtx, interval, acmeClient, cfg.Server.MainDomain, certDB) if cfg.Server.HttpServerEnabled { // Create handler for http->https redirect and http acme challenges diff --git a/server/upstream/domains.go b/server/upstream/domains.go index d53d586..f68a02b 100644 --- a/server/upstream/domains.go +++ b/server/upstream/domains.go @@ -8,6 +8,7 @@ import ( "github.com/rs/zerolog/log" "codeberg.org/codeberg/pages/server/cache" + "codeberg.org/codeberg/pages/server/context" "codeberg.org/codeberg/pages/server/gitea" ) @@ -17,7 +18,7 @@ var canonicalDomainCacheTimeout = 15 * time.Minute const canonicalDomainConfig = ".domains" // CheckCanonicalDomain returns the canonical domain specified in the repo (using the `.domains` file). -func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) { +func (o *Options) CheckCanonicalDomain(ctx *context.Context, giteaClient *gitea.Client, actualDomain, mainDomainSuffix string, canonicalDomainCache cache.ICache) (domain string, valid bool) { // Check if this request is cached. if cachedValue, ok := canonicalDomainCache.Get(o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch); ok { domains := cachedValue.([]string) @@ -30,7 +31,7 @@ func (o *Options) CheckCanonicalDomain(giteaClient *gitea.Client, actualDomain, return domains[0], valid } - body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) + body, err := giteaClient.GiteaRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, canonicalDomainConfig) if err != nil && !errors.Is(err, gitea.ErrorNotFound) { log.Error().Err(err).Msgf("could not read %s of %s/%s", canonicalDomainConfig, o.TargetOwner, o.TargetRepo) } diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index ddde375..3ed5010 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -44,7 +44,7 @@ var redirectsCacheTimeout = 10 * time.Minute const redirectsConfig = "_redirects" // getRedirects returns redirects specified in the _redirects file. -func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect { +func (o *Options) getRedirects(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) []Redirect { var redirects []Redirect cacheKey := o.TargetOwner + "/" + o.TargetRepo + "/" + o.TargetBranch @@ -53,7 +53,7 @@ func (o *Options) getRedirects(giteaClient *gitea.Client, redirectsCache cache.I redirects = cachedValue.([]Redirect) } else { // Get _redirects file and parse - body, err := giteaClient.GiteaRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig) + body, err := giteaClient.GiteaRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, redirectsConfig) if err == nil { for _, line := range strings.Split(string(body), "\n") { redirectArr := strings.Fields(line) diff --git a/server/upstream/upstream.go b/server/upstream/upstream.go index 2abd0b9..9aac271 100644 --- a/server/upstream/upstream.go +++ b/server/upstream/upstream.go @@ -125,7 +125,7 @@ func AcceptEncodings(header string) []string { // Upstream requests a file from the Gitea API at GiteaRoot and writes it to the request context. func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redirectsCache cache.ICache) bool { - log := log.With().Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() + log := log.With().Str("ReqId", ctx.ReqId).Strs("upstream", []string{o.TargetOwner, o.TargetRepo, o.TargetBranch, o.TargetPath}).Logger() log.Debug().Msg("Start") @@ -182,7 +182,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi // add extension for encoding path := o.TargetPath + allowedEncodings[encoding] - reader, header, statusCode, err = giteaClient.ServeRawContent(o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) + reader, header, statusCode, err = giteaClient.ServeRawContent(ctx, o.TargetOwner, o.TargetRepo, o.TargetBranch, path, true) if statusCode == http.StatusNotFound { continue } @@ -206,7 +206,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi if err != nil && errors.Is(err, gitea.ErrorNotFound) { log.Debug().Msg("Handling not found error") // Get and match redirects - redirects := o.getRedirects(giteaClient, redirectsCache) + redirects := o.getRedirects(ctx, giteaClient, redirectsCache) if o.matchRedirects(ctx, giteaClient, redirects, redirectsCache) { log.Trace().Msg("redirect") return true @@ -234,7 +234,7 @@ func (o *Options) Upstream(ctx *context.Context, giteaClient *gitea.Client, redi } } - log.Trace().Msg("not found") + log.Debug().Msg("not found") ctx.StatusCode = http.StatusNotFound if o.TryIndexPages { From 6376bfd2e0e0b64744731aaa8519c1c19fd04853 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Dec 2024 19:01:08 +0000 Subject: [PATCH 291/320] Fix stack overflow on root redirects (#414) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/414 Co-authored-by: crapStone Co-committed-by: crapStone --- server/upstream/redirects.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/upstream/redirects.go b/server/upstream/redirects.go index 3ed5010..b0762d5 100644 --- a/server/upstream/redirects.go +++ b/server/upstream/redirects.go @@ -92,8 +92,9 @@ func (o *Options) matchRedirects(ctx *context.Context, giteaClient *gitea.Client for _, redirect := range redirects { if dstURL, ok := redirect.rewriteURL(reqURL); ok { - // do rewrite if status code is 200 - if redirect.StatusCode == 200 { + if o.TargetPath == dstURL { // recursion base case, rewrite directly when paths are the same + return true + } else if redirect.StatusCode == 200 { // do rewrite if status code is 200 o.TargetPath = dstURL o.Upstream(ctx, giteaClient, redirectsCache) } else { From 229851b75e888ce504c703e306e3af7fd2c43b1d Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Dec 2024 20:10:27 +0000 Subject: [PATCH 292/320] chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.2 (#409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-release](https://codeberg.org/woodpecker-plugins/release) ([source](https://codeberg.org/woodpecker-plugins/release.git)) | patch | `0.2.1` -> `0.2.2` | --- ### Release Notes
woodpecker-plugins/release (woodpeckerci/plugin-release) ### [`v0.2.2`](https://codeberg.org/woodpecker-plugins/release/blob/HEAD/CHANGELOG.md#022---2024-11-27) [Compare Source](https://codeberg.org/woodpecker-plugins/release/compare/v0.2.1...v0.2.2) ##### ❤️ Thanks to all contributors! ❤️ [@​6543](https://github.com/6543), [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 📦️ Dependency - fix(deps): update module github.com/google/go-github/v65 to v66 \[[#​20](https://codeberg.org/woodpecker-plugins/release/pulls/20)] - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5 \[[#​19](https://codeberg.org/woodpecker-plugins/release/pulls/19)] - fix(deps): update module github.com/google/go-github/v64 to v65 \[[#​16](https://codeberg.org/woodpecker-plugins/release/pulls/16)] - fix(deps): update module github.com/google/go-github/v63 to v64 \[[#​15](https://codeberg.org/woodpecker-plugins/release/pulls/15)] - chore(deps): update dependency go to v1.23.0 \[[#​14](https://codeberg.org/woodpecker-plugins/release/pulls/14)] ##### Misc - Use ready-release-go plugin \[[#​17](https://codeberg.org/woodpecker-plugins/release/pulls/17)]
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/409 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 10489fb..07d25b5 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -62,7 +62,7 @@ steps: release: depends_on: build - image: woodpeckerci/plugin-release:0.2.1 + image: woodpeckerci/plugin-release:0.2.2 settings: base_url: https://codeberg.org file_exists: overwrite From 6fd9474075697e640da3c04fdd001cc50f01c308 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Dec 2024 20:10:49 +0000 Subject: [PATCH 293/320] chore(deps): update pipelinecomponents/yamllint docker tag to v0.33.0 (#410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [pipelinecomponents/yamllint](https://gitlab.com/pipeline-components/yamllint) | minor | `0.32.1` -> `0.33.0` | --- ### Release Notes
pipeline-components/yamllint (pipelinecomponents/yamllint) ### [`v0.33.0`](https://gitlab.com/pipeline-components/yamllint/compare/v0.32.1...v0.33.0) [Compare Source](https://gitlab.com/pipeline-components/yamllint/compare/v0.32.1...v0.33.0)
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/410 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index d8ef836..7b6c479 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -18,7 +18,7 @@ steps: image: mstruebing/editorconfig-checker:v3.0.3 yamllint: - image: pipelinecomponents/yamllint:0.32.1 + image: pipelinecomponents/yamllint:0.33.0 depends_on: [] commands: - yamllint . From 905e76deedae3414858a286e83337cb611ad6c77 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 29 Dec 2024 20:16:51 +0000 Subject: [PATCH 294/320] chore(deps): update golangci/golangci-lint docker tag to v1.62.2 (#408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | patch | `v1.62.0` -> `v1.62.2` | --- ### Release Notes
golangci/golangci-lint (golangci/golangci-lint) ### [`v1.62.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1622) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.62.0...v1.62.2) 1. Updated linters - `fatcontext`: from 0.5.2 to 0.5.3 - `ginkgolinter`: from 0.18.0 to 0.18.3 - `go-errorlint`: from 1.6.0 to 1.7.0 - `iface`: from 1.2.0 to 1.2.1 - `revive`: from 1.5.0 to 1.5.1 - `testifylint`: from 1.5.0 to 1.5.2 2. Misc. - fix: ignore cache error when file not found
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/408 Reviewed-by: crapStone Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 7b6c479..8b18851 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.62.0 + image: golangci/golangci-lint:v1.62.2 commands: - go version - go install mvdan.cc/gofumpt@latest From e8f9ec9ce28ee8c5acc3e181d85ab5566941934b Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Dec 2024 20:54:45 +0000 Subject: [PATCH 295/320] Update dependencies (#406) closes #299 #395 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/406 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 8 +- go.mod | 289 +++-- go.sum | 2333 +++++++++++++++++++++++++++++++---------- 3 files changed, 1984 insertions(+), 646 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 07d25b5..60f3311 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -11,7 +11,7 @@ steps: build: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just:go-1.22 + image: codeberg.org/6543/docker-images/golang_just:go-1.23 commands: - go version - just build @@ -32,7 +32,7 @@ steps: build-tag: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just:go-1.22 + image: codeberg.org/6543/docker-images/golang_just:go-1.23 commands: - go version - just build-tag ${CI_COMMIT_TAG##v} @@ -41,7 +41,7 @@ steps: test: depends_on: build - image: codeberg.org/6543/docker-images/golang_just:go-1.22 + image: codeberg.org/6543/docker-images/golang_just:go-1.23 commands: - just test when: @@ -49,7 +49,7 @@ steps: integration-tests: depends_on: build - image: codeberg.org/6543/docker-images/golang_just:go-1.22 + image: codeberg.org/6543/docker-images/golang_just:go-1.23 commands: - just integration environment: diff --git a/go.mod b/go.mod index 1bc1de6..13e40df 100644 --- a/go.mod +++ b/go.mod @@ -1,145 +1,238 @@ module codeberg.org/codeberg/pages -go 1.22.0 +go 1.23.0 -toolchain go1.23.1 +toolchain go1.23.3 require ( - code.gitea.io/sdk/gitea v0.17.1 + code.gitea.io/sdk/gitea v0.19.0 github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a - github.com/creasty/defaults v1.7.0 - github.com/go-acme/lego/v4 v4.5.3 - github.com/go-sql-driver/mysql v1.6.0 + github.com/creasty/defaults v1.8.0 + github.com/go-acme/lego/v4 v4.21.0 + github.com/go-sql-driver/mysql v1.8.1 + github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/joho/godotenv v1.4.0 - github.com/lib/pq v1.10.7 - github.com/mattn/go-sqlite3 v1.14.16 - github.com/microcosm-cc/bluemonday v1.0.26 - github.com/pelletier/go-toml/v2 v2.1.0 + github.com/joho/godotenv v1.5.1 + github.com/lib/pq v1.10.9 + github.com/mattn/go-sqlite3 v1.14.24 + github.com/microcosm-cc/bluemonday v1.0.27 + github.com/pelletier/go-toml/v2 v2.2.3 github.com/pires/go-proxyproto v0.8.0 github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad - github.com/rs/zerolog v1.27.0 - github.com/stretchr/testify v1.8.4 - github.com/urfave/cli/v2 v2.3.0 - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f - xorm.io/xorm v1.3.2 + github.com/rs/zerolog v1.33.0 + github.com/stretchr/testify v1.10.0 + github.com/urfave/cli/v2 v2.27.5 + golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 + xorm.io/xorm v1.3.9 ) require ( - cloud.google.com/go v0.54.0 // indirect - github.com/Azure/azure-sdk-for-go v32.4.0+incompatible // indirect + cloud.google.com/go/auth v0.13.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.19 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect + github.com/Azure/go-autorest/autorest v0.11.29 // indirect + github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect + github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect + github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect - github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect - github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect - github.com/aws/aws-sdk-go v1.39.0 // indirect + github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect + github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect + github.com/aws/smithy-go v1.22.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect - github.com/cenkalti/backoff/v4 v4.1.1 // indirect - github.com/cloudflare/cloudflare-go v0.20.0 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/boombuler/barcode v1.0.2 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/civo/civogo v0.3.89 // indirect + github.com/cloudflare/cloudflare-go v0.112.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect - github.com/deepmap/oapi-codegen v1.6.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/dnsimple/dnsimple-go v0.70.1 // indirect - github.com/exoscale/egoscale v0.67.0 // indirect + github.com/dnsimple/dnsimple-go v1.7.0 // indirect + github.com/exoscale/egoscale/v3 v3.1.7 // indirect github.com/fatih/structs v1.1.0 // indirect - github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect - github.com/go-errors/errors v1.0.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-errors/errors v1.5.1 // indirect github.com/go-fed/httpsig v1.1.0 // indirect - github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect - github.com/goccy/go-json v0.8.1 // indirect - github.com/gofrs/uuid v4.0.0+incompatible // indirect - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.23.0 // indirect + github.com/go-resty/resty/v2 v2.16.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/goccy/go-json v0.10.4 // indirect + github.com/gofrs/flock v0.12.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/gax-go/v2 v2.0.5 // indirect - github.com/gophercloud/gophercloud v0.16.0 // indirect - github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect - github.com/gorilla/css v1.0.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.0 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect + github.com/gophercloud/gophercloud v1.14.1 // indirect + github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect + github.com/gorilla/css v1.0.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect - github.com/jarcoal/httpmock v1.0.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect - github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/goh v1.0.1 // indirect - github.com/linode/linodego v0.31.1 // indirect - github.com/liquidweb/go-lwApi v0.0.5 // indirect - github.com/liquidweb/liquidweb-cli v0.6.9 // indirect - github.com/liquidweb/liquidweb-go v1.6.3 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/miekg/dns v1.1.43 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/linode/linodego v1.44.0 // indirect + github.com/liquidweb/liquidweb-cli v0.7.0 // indirect + github.com/liquidweb/liquidweb-go v1.6.4 // indirect + github.com/magiconair/properties v1.8.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/miekg/dns v1.1.62 // indirect + github.com/mimuret/golang-iij-dpf v0.9.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect - github.com/nrdcg/auroradns v1.0.1 // indirect - github.com/nrdcg/desec v0.6.0 // indirect + github.com/nrdcg/auroradns v1.1.0 // indirect + github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect + github.com/nrdcg/desec v0.10.0 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect - github.com/nrdcg/freemyip v0.2.0 // indirect - github.com/nrdcg/goinwx v0.8.1 // indirect + github.com/nrdcg/freemyip v0.3.0 // indirect + github.com/nrdcg/goinwx v0.10.0 // indirect + github.com/nrdcg/mailinabox v0.2.0 // indirect github.com/nrdcg/namesilo v0.2.1 // indirect - github.com/nrdcg/porkbun v0.1.1 // indirect - github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect - github.com/ovh/go-ovh v1.1.0 // indirect + github.com/nrdcg/nodion v0.1.0 // indirect + github.com/nrdcg/porkbun v0.4.0 // indirect + github.com/nzdjb/go-metaname v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect + github.com/oracle/oci-go-sdk/v65 v65.81.1 // indirect + github.com/ovh/go-ovh v1.6.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/peterhellberg/link v1.2.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/pquerna/otp v1.3.0 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect - github.com/sacloud/libsacloud v1.36.2 // indirect - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - github.com/sirupsen/logrus v1.4.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/pquerna/otp v1.4.0 // indirect + github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sacloud/api-client-go v0.2.10 // indirect + github.com/sacloud/go-http v0.1.9 // indirect + github.com/sacloud/iaas-api-go v1.14.0 // indirect + github.com/sacloud/packages-go v0.0.11 // indirect + github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect + github.com/selectel/domains-go v1.1.0 // indirect + github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect - github.com/softlayer/softlayer-go v1.0.3 // indirect + github.com/softlayer/softlayer-go v1.1.7 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect - github.com/spf13/cast v1.3.1 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/sony/gobreaker v1.0.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect - github.com/transip/gotransip/v6 v6.6.1 // indirect - github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 // indirect - github.com/vultr/govultr/v2 v2.7.1 // indirect - go.opencensus.io v0.22.3 // indirect - go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect - google.golang.org/api v0.20.0 // indirect - google.golang.org/appengine v1.6.5 // indirect - google.golang.org/genproto v0.0.0-20200305110556-506484158171 // indirect - google.golang.org/grpc v1.27.1 // indirect - google.golang.org/protobuf v1.26.0 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071 // indirect + github.com/tjfoc/gmsm v1.4.1 // indirect + github.com/transip/gotransip/v6 v6.26.0 // indirect + github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect + github.com/vinyldns/go-vinyldns v0.9.16 // indirect + github.com/volcengine/volc-sdk-golang v1.0.190 // indirect + github.com/vultr/govultr/v3 v3.13.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect + github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect + go.mongodb.org/mongo-driver v1.17.1 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/ratelimit v0.3.1 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.8.0 // indirect + golang.org/x/tools v0.28.0 // indirect + google.golang.org/api v0.214.0 // indirect + google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect + google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.36.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - xorm.io/builder v0.3.12 // indirect + k8s.io/api v0.32.0 // indirect + k8s.io/apimachinery v0.32.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect + xorm.io/builder v0.3.13 // indirect ) diff --git a/go.sum b/go.sum index 6aef8a6..f8ab2be 100644 --- a/go.sum +++ b/go.sum @@ -3,508 +3,1280 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= +cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= -code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= +code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= -gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= +github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= +github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.19 h1:7/IqD2fEYVha1EPeaiytVKhzmPV223pfkRIQUGOK2IE= -github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= -github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 h1:TzPg6B6fTZ0G1zBf3T54aI7p3cAT6u//TOXGPmFMOXg= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= +github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= +github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= +github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4= +github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= +github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a h1:Cf4CrDeyrIcuIiJZEZJAH5dapqQ6J3OmP/vHPbDjaFA= github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a/go.mod h1:ig6eVXkYn/9dz0Vm8UdLf+E0u1bE6kBSn3n2hqk6jas= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 h1:bLzehmpyCwQiqCE1Qe9Ny6fbFqs7hPlmo9vKv2orUxs= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 h1:dkj8/dxOQ4L1XpwCzRLqukvUBbxuNdz3FeyvHFnRjmo= -github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 h1:mg/+23+/gAw6zdxv9I5dPCj666WJPLk8S1nXm0dOumQ= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.76/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.39.0 h1:74BBwkEmiqBbi2CGflEh34l0YNtIibTjZsibGarkNjo= -github.com/aws/aws-sdk-go v1.39.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= +github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= +github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= +github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 h1:+lmJoqxuUoPlSfGk5JYQQivd9YFjUvRZR6RPY+Wcx48= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8/go.mod h1:Gg8/myP4+rgRi4+j9gQdbOEnMtwMAUUIeXo+nKCFVj8= +github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 h1:0jMtawybbfpFEIMy4wvfyW2Z4YLr7mnuzT0fhR67Nrc= +github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4/go.mod h1:xlMODgumb0Pp8bzfpojqelDrf8SL9rb5ovwmwKJl+oU= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4= +github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1 h1:G2HAfAmvm/GcKan2oOQpBXOd2tT2G57ZnZGWa1PxPBQ= +github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/civo/civogo v0.3.89 h1:g+I4NGVa5t0L2Z9+QbnEAqxE/3OCDUYvepje3oUkKVo= +github.com/civo/civogo v0.3.89/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.20.0 h1:y2a6KwYHTFxhw+8PLhz0q5hpTGj6Un3W1pbpQLhzFaE= -github.com/cloudflare/cloudflare-go v0.20.0/go.mod h1:sPWL/lIC6biLEdyGZwBQ1rGQKF1FhM7N60fuNiFdYTI= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g= +github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4= github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= -github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= +github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= -github.com/deepmap/oapi-codegen v1.6.1 h1:2BvsmRb6pogGNtr8Ann+esAbSKFXx2CZN18VpAMecnw= -github.com/deepmap/oapi-codegen v1.6.1/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/dnsimple/dnsimple-go v0.70.1 h1:cSZndVjttLpgplDuesY4LFIvfKf/zRA1J7mCATBbzSM= -github.com/dnsimple/dnsimple-go v0.70.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= +github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/exoscale/egoscale v0.67.0 h1:qgWh7T5IZGrNWtg6ib4dr+76WThvB+odTtGG+DGbXF8= -github.com/exoscale/egoscale v0.67.0/go.mod h1:wi0myUxPsV8SdEtdJHQJxFLL/wEw9fiw9Gs1PWRkvkM= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/exoscale/egoscale/v3 v3.1.7 h1:Q6p9tOVY0IiOW0fUpaPQWY7ggGEuSPZLAGxFgDd2sCE= +github.com/exoscale/egoscale/v3 v3.1.7/go.mod h1:GHKucK/J26v8PGWztGdhxWNMjrjG9PbelxKCJ4YI11Q= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= +github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v4 v4.5.3 h1:v5RSN8l+RAeNHKTSL80eqLiec6q6UNaFpl2Df5x/5tM= -github.com/go-acme/lego/v4 v4.5.3/go.mod h1:mL1DY809LzjvRuaxINNxsI26f5oStVhBGTpJMiinkZM= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o= +github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4up0dUeDI= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= +github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= +github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= -github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= -github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI= -github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v0.16.0 h1:sWjPfypuzxRxjVbk3/MsU4H8jS0NNlyauZtIUl78BPU= -github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15Kfovd8MTZrcana+UlQqNbOif8dLpA0= -github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw= +github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 h1:sH7xkTfYzxIEgzq1tDHIMKRh1vThOEOGNsettdEeLbE= +github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56/go.mod h1:VSalo4adEk+3sNkmVJLnhHoOyOYYS8sTWLG4mv5BKto= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= +github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129 h1:+4ocCqiQFWTODV2Q94q/DMGrmoXFIrimPn+SZZPCbBs= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= -github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= -github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= -github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= -github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= -github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= -github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g= -github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= -github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= -github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= -github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= -github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= -github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= -github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= -github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= -github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= -github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= -github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE= -github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE= -github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= -github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= -github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= -github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= -github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= -github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc= -github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60= -github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= -github.com/jarcoal/httpmock v1.0.6 h1:e81vOSexXU3mJuJ4l//geOmKIt+Vkxerk1feQBC8D0g= -github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= -github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/linode/linodego v0.31.1 h1:dBtjKo7J9UhNFhTOclEXb12RRyQDaRBxISdONVuU+DA= -github.com/linode/linodego v0.31.1/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/linode/linodego v1.44.0 h1:JZLLWzCAx3CmHSV9NmCoXisuqKtrmPhfY9MrgvaHMUY= +github.com/linode/linodego v1.44.0/go.mod h1:umdoNOmtbqAdGQbmQnPFZ2YS4US+/mU/1bA7MjoKAvg= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= -github.com/liquidweb/go-lwApi v0.0.5 h1:CT4cdXzJXmo0bon298kS7NeSk+Gt8/UHpWBBol1NGCA= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= -github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= -github.com/liquidweb/liquidweb-go v1.6.3 h1:NVHvcnX3eb3BltiIoA+gLYn15nOpkYkdizOEYGSKrk4= -github.com/liquidweb/liquidweb-go v1.6.3/go.mod h1:SuXXp+thr28LnjEw18AYtWwIbWMHSUiajPQs8T9c/Rc= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/liquidweb/liquidweb-cli v0.7.0 h1:7j1r1U0MZa1TXiWo3IMU5V1YQwnBHMVxU+xLsgDcu6Q= +github.com/liquidweb/liquidweb-cli v0.7.0/go.mod h1:+uU7L6BhaQtgo4cYKhhsP5UNCq/imNvjBjlf76Vqpb0= +github.com/liquidweb/liquidweb-go v1.6.4 h1:6S0m3hHSpiLqGD7AFSb7lH/W/qr1wx+tKil9fgIbjMc= +github.com/liquidweb/liquidweb-go v1.6.4/go.mod h1:B934JPIIcdA+uTq2Nz5PgOtG6CuCaEvQKe/Ge/5GgZ4= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= -github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= +github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= +github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= +github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= +github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34= +github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -512,289 +1284,426 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= +github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= +github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y= -github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= -github.com/nrdcg/desec v0.6.0 h1:kZ9JtsYEW3LNfuPIM+2tXoxoQlF9koWfQTWTQsA7Sr8= -github.com/nrdcg/desec v0.6.0/go.mod h1:wybWg5cRrNmtXLYpUCPCLvz4jfFNEGZQEnoUiX9WqcY= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo= +github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8= +github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM= +github.com/nrdcg/desec v0.10.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= -github.com/nrdcg/freemyip v0.2.0 h1:/GscavT4GVqAY13HExl5UyoB4wlchv6Cg5NYDGsUoJ8= -github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4= -github.com/nrdcg/goinwx v0.8.1 h1:20EQ/JaGFnSKwiDH2JzjIpicffl3cPk6imJBDqVBVtU= -github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c= +github.com/nrdcg/freemyip v0.3.0 h1:0D2rXgvLwe2RRaVIjyUcQ4S26+cIS2iFwnhzDsEuuwc= +github.com/nrdcg/freemyip v0.3.0/go.mod h1:c1PscDvA0ukBF0dwelU/IwOakNKnVxetpAQ863RMJoM= +github.com/nrdcg/goinwx v0.10.0 h1:6W630bjDxQD6OuXKqrFRYVpTt0G/9GXXm3CeOrN0zJM= +github.com/nrdcg/goinwx v0.10.0/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4= +github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk= +github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc= github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= -github.com/nrdcg/porkbun v0.1.1 h1:gxVzQYfFUGXhnBax/aVugoE3OIBAdHgrJgyMPyY5Sjo= -github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA= +github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw= +github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms= +github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw= +github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/nzdjb/go-metaname v1.0.0 h1:sNASlZC1RM3nSudtBTE1a3ZVTDyTpjqI5WXRPrdZ9Hg= +github.com/nzdjb/go-metaname v1.0.0/go.mod h1:0GR0LshZax1Lz4VrOrfNSE4dGvTp7HGjiemdczXT2H4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= -github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= -github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= -github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/oracle/oci-go-sdk/v65 v65.81.1 h1:JYc47bk8n/MUchA2KHu1ggsCQzlJZQLJ+tTKfOho00E= +github.com/oracle/oci-go-sdk/v65 v65.81.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI= +github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= +github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= +github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0= github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= -github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= +github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE= +github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= -github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sacloud/libsacloud v1.36.2 h1:aosI7clbQ9IU0Hj+3rpk3SKJop5nLPpLThnWCivPqjI= -github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f h1:WSnaD0/cvbKJgSTYbjAPf4RJXVvNNDAwVm+W8wEmnGE= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sacloud/api-client-go v0.2.10 h1:+rv3jDohD+pkdYwOTBiB+jZsM0xK3AxadXRzhp3q66c= +github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc= +github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE= +github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE= +github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ= +github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0= +github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= +github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= +github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/selectel/domains-go v1.1.0 h1:futG50J43ALLKQAnZk9H9yOtLGnSUh7c5hSvuC5gSHo= +github.com/selectel/domains-go v1.1.0/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA= +github.com/selectel/go-selvpcclient/v3 v3.2.1 h1:ny6WIAMiHzKxOgOEnwcWE79wIQij1AHHylzPA41MXCw= +github.com/selectel/go-selvpcclient/v3 v3.2.1/go.mod h1:3EfSf8aEWyhspOGbvZ6mvnFg7JN5uckxNyBFPGWsXNQ= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= -github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= -github.com/softlayer/softlayer-go v1.0.3 h1:9FONm5xzQ9belQtbdryR6gBg4EF6hX6lrjNKi0IvZkU= -github.com/softlayer/softlayer-go v1.0.3/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4= +github.com/softlayer/softlayer-go v1.1.7 h1:SgTL+pQZt1h+5QkAhVmHORM/7N9c1X0sljJhuOIHxWE= +github.com/softlayer/softlayer-go v1.1.7/go.mod h1:WeJrBLoTJcaT8nO1azeyHyNpo/fDLtbpbvh+pzts+Qw= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= +github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071 h1:Q/Ue/yRv4HSpaiFAnXIshoDjxzwyhwezEidXU49Boa4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071 h1:1P0aq3ss/sku5n2qq18qTeyApQVMcy9/b7qbXPejCSc= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071/go.mod h1:wIqmfnhHUG/GMIK4OfsvXA29+Ei4Gmz5K3SWftLVxFQ= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/transip/gotransip/v6 v6.6.1 h1:nsCU1ErZS5G0FeOpgGXc4FsWvBff9GPswSMggsC4564= -github.com/transip/gotransip/v6 v6.6.1/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= -github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo= -github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550= +github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec h1:2s/ghQ8wKE+UzD/hf3P4Gd1j0JI9ncbxv+nsypPoUYI= +github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7Qs3ku1ckpqed8tCRSqTlp8NAeZfAVpfx4OzXMss= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE= -github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= -github.com/vultr/govultr/v2 v2.7.1 h1:uF9ERet++Gb+7Cqs3p1P6b6yebeaZqVd7t5P2uZCaJU= -github.com/vultr/govultr/v2 v2.7.1/go.mod h1:BvOhVe6/ZpjwcoL6/unkdQshmbS9VGbowI4QT+3DGVU= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= +github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= +github.com/volcengine/volc-sdk-golang v1.0.190 h1:bva2jbMuce9LIqWRlfWbZcyriRs17ZaSoLZulMpk6K8= +github.com/volcengine/volc-sdk-golang v1.0.190/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= +github.com/vultr/govultr/v3 v3.13.0 h1:Tq26syAju/cTlgorJJX391ZTbhILbe4WzIatyeStXCM= +github.com/vultr/govultr/v3 v3.13.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c h1:Rnr+lDYXVkP+3eT8/d68iq4G/UeIhyCQk+HKa8toTvg= +github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= +github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 h1:qmpz0Kvr9GAng8LAhRcKIpY71CEAcL3EBkftVlsP5Cw= +github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134/go.mod h1:KgZCJrxdhdw/sKhTQ/M3S9WOLri2PCnBlc4C3s+PfKY= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq9BgzFU6s= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 h1:d9qaMM+ODpCq+9We41//fu/sHsTnXcrqd1en3x+GKy4= -go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= +go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= +golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -805,6 +1714,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -813,8 +1724,15 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -825,7 +1743,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -836,46 +1753,117 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -883,11 +1871,10 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -896,56 +1883,121 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201110211018-35f3e6cf4a65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -955,40 +2007,50 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -997,7 +2059,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1005,21 +2066,57 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= -golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1029,21 +2126,69 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0 h1:jz2KixHX7EcCPiQrySzPdnYT7DbINAypCqKZ1Z7GM40= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= +google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= +google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -1056,54 +2201,213 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171 h1:xes2Q2k+d/+YNXVw0FpZkIDJiaux4OVrRKXRAzH6A0U= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= +google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 h1:e26eS1K69yxjjNNHYqjN49y95kcaQLJ3TL5h68dcA1E= +google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:i5btTErZyoKCCubju3HS5LVho4nZd3yFnEp6moqeUjE= +google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0= +google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= -gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ns1/ns1-go.v2 v2.6.2 h1:tC+gRSN6fmnb9l9cVrIysXyuRO0wV6cZrjDqlMB0gGc= -gopkg.in/ns1/ns1-go.v2 v2.6.2/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48= +gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -1111,137 +2415,78 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= +k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U= -modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= -modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= -modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw= -modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI= -modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag= -modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw= -modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ= -modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c= -modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo= -modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg= -modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I= -modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs= -modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8= -modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE= -modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk= -modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w= -modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE= -modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8= -modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc= -modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU= -modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE= -modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk= -modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI= -modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE= -modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg= -modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74= -modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU= -modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU= -modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc= -modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM= -modernc.org/ccgo/v3 v3.12.65/go.mod h1:D6hQtKxPNZiY6wDBtehSGKFKmyXn53F8nGTpH+POmS4= -modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ= -modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84= -modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ= -modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= -modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8= -modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= -modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= -modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= -modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= -modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= -modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= -modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= -modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso= -modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8= -modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8= -modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I= -modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk= -modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY= -modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE= -modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg= -modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM= -modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg= -modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo= -modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8= -modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ= -modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA= -modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM= -modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg= -modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE= -modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM= -modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU= -modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw= -modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M= -modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18= -modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8= -modernc.org/libc v1.11.70/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= -modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw= -modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0= -modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI= -modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= -modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE= -modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY= -modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= -modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14= -modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= -modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE= -modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8= -modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY= -modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM= -xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/xorm v1.3.2 h1:uTRRKF2jYzbZ5nsofXVUx6ncMaek+SHjWYtCXyZo1oM= -xorm.io/xorm v1.3.2/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= +sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= +xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/xorm v1.3.9 h1:TUovzS0ko+IQ1XnNLfs5dqK1cJl1H5uHpWbWqAQ04nU= +xorm.io/xorm v1.3.9/go.mod h1:LsCCffeeYp63ssk0pKumP6l96WZcHix7ChpurcLNuMw= From 2f6960b88aa7a875218f61d3cef9018ab66663f4 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sun, 29 Dec 2024 21:12:17 +0000 Subject: [PATCH 296/320] fix woodpecker (#415) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/415 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 60f3311..19f9cc4 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -53,10 +53,10 @@ steps: commands: - just integration environment: - - ACME_API=https://acme.mock.directory - - PAGES_DOMAIN=localhost.mock.directory - - RAW_DOMAIN=raw.localhost.mock.directory - - PORT=4430 + ACME_API: https://acme.mock.directory + PAGES_DOMAIN: localhost.mock.directory + RAW_DOMAIN: raw.localhost.mock.directory + PORT: 4430 when: - event: [pull_request] From 079fd09c43e7a5b432054ffe1dc4c68a497dfc84 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 17 Jan 2025 19:16:56 +0000 Subject: [PATCH 297/320] chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.1.0 (#421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-docker-buildx](https://codeberg.org/woodpecker-plugins/docker-buildx) ([source](https://codeberg.org/woodpecker-plugins/docker-buildx.git)) | minor | `5.0.0` -> `5.1.0` | --- ### Release Notes
woodpecker-plugins/docker-buildx (woodpeckerci/plugin-docker-buildx) ### [`v5.1.0`](https://codeberg.org/woodpecker-plugins/docker-buildx/blob/HEAD/CHANGELOG.md#510---2024-12-07) [Compare Source](https://codeberg.org/woodpecker-plugins/docker-buildx/compare/v5.0.0...v5.1.0) ##### ❤️ Thanks to all contributors! ❤️ [@​6543](https://github.com/6543), [@​pat-s](https://github.com/pat-s), [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 📈 Enhancement - Allow build-args to get json \[[#​202](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/202)] ##### 📚 Documentation - Clarify insecure tag \[[#​200](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/200)] ##### 📦️ Dependency - fix(deps): update module github.com/urfave/cli/v3 to v3.0.0-alpha9.1 \[[#​199](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/199)] - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.0.0 \[[#​198](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/198)] ##### Misc - chore(deps): update docker/buildx-bin docker tag to v0.19.1 ([5ff83b1](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/5ff83b1454f72ce243b95140fa5f7d8d1193d625)) - fix(deps): update module github.com/urfave/cli/v3 to v3.0.0-beta1 ([62b3316](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/62b3316cb606bc404a063e705382b9f6a410b45e)) - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.2 ([2ff549c](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/2ff549c3c6243863fae9aca26fd6b3a19dee6540)) - fix(deps): update module github.com/stretchr/testify to v1.10.0 ([ed1d55f](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/ed1d55f533cc6e49c65656adf2bb8484f93f5b5c)) - chore(deps): update docker/buildx-bin docker tag to v0.18.0 ([43bbfaa](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/43bbfaa0af080b89ae18075d11828d8d7776d0b3)) - chore(deps): update docker.io/woodpeckerci/plugin-ready-release-go docker tag to v2.1.1 ([faf4a88](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/faf4a889f6f167a23d7a95db99e174b8fa776614)) - fix(deps): update module github.com/urfave/cli/v3 to v3.0.0-alpha9.4 ([1d723a3](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/1d723a337972a4cc6dc8e13748831cc5c055c7e1)) - chore(deps): update davidanson/markdownlint-cli2 docker tag to v0.15.0 ([f6bbd6c](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/f6bbd6c1c2a49d5c883ce9cbe76a543ab4762a14)) - fix(deps): update module github.com/urfave/cli/v3 to v3.0.0-alpha9.3 ([8d3d558](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/8d3d558c0975c074c34af88717a8facc13cfbd55)) - chore(deps): update dependency go to v1.23.3 ([be45ede](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/be45ede5004f5d4cc7249fb8590acaf6942ff8c9)) - fix(deps): update golang.org/x/lint digest to [`818c5a8`](https://github.com/woodpecker-plugins/docker-buildx/commit/818c5a8) ([cbf2022](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/cbf20222b0c343a827ae19c9f52b33d602a67adf)) - Add vendor target for make \[[#​201](https://codeberg.org/woodpecker-plugins/docker-buildx/pulls/201)] - remove pat-s from maintainers ([b664c37](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/b664c37d62e2bd12683f7769324050942678534b)) - CI: Dont lint on tag event ([1f4e506](https://codeberg.org/woodpecker-plugins/docker-buildx/src/commit/1f4e506a190a102c611769e2482442a14e4e131e))
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/421 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 19f9cc4..26ba76f 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -20,7 +20,7 @@ steps: docker-dryrun: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.0.0 + image: woodpeckerci/plugin-docker-buildx:5.1.0 settings: dockerfile: Dockerfile platforms: linux/amd64 @@ -74,7 +74,7 @@ steps: docker-next: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.0.0 + image: woodpeckerci/plugin-docker-buildx:5.1.0 settings: registry: codeberg.org dockerfile: Dockerfile @@ -89,7 +89,7 @@ steps: - event: [push] 'Publish PR image': - image: woodpeckerci/plugin-docker-buildx:5.0.0 + image: woodpeckerci/plugin-docker-buildx:5.1.0 depends_on: test settings: registry: codeberg.org @@ -107,7 +107,7 @@ steps: docker-release: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.0.0 + image: woodpeckerci/plugin-docker-buildx:5.1.0 settings: registry: codeberg.org dockerfile: Dockerfile From b62c6bdd2d71b828d601b0c7b072079204c52e3e Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 17 Jan 2025 19:17:14 +0000 Subject: [PATCH 298/320] chore(deps): update golangci/golangci-lint docker tag to v1.63.4 (#419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | minor | `v1.62.2` -> `v1.63.4` | --- ### Release Notes
golangci/golangci-lint (golangci/golangci-lint) ### [`v1.63.4`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1634) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.3...v1.63.4) 1. Linters bug fixes - `dupl`, `gomodguard`, `revive`: keep only Go-files. ### [`v1.63.3`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1633) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.2...v1.63.3) 1. Linters bug fixes - `gofmt`, `gofumpt`, `goimports`, `gci`: panic with several trailing EOL - `goheader`: skip issues with invalid positions ### [`v1.63.2`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1632) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.1...v1.63.2) 1. Linters bug fixes - `gofmt`, `gofumpt`, `goimports`, `gci`: panic with missing trailing EOL ### [`v1.63.1`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1631) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.63.0...v1.63.1) 1. Linters bug fixes - `cgi`: invalid reports with cgo - `gofumpt`: panic with autofix and cgo ### [`v1.63.0`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1630) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.62.2...v1.63.0) 1. Enhancements - Add support for SuggestedFixes 🎉 (35 linters can "autofix" reports). - Formatters (`gofmt`, `goimports`, `gofumpt`, `gci`) are applied after the suggested fixes. 2. New linters - Add `exptostd` linter https://github.com/ldez/exptostd - Add `nilnesserr` linter https://github.com/alingse/nilnesserr - Add `usetesting` linter https://github.com/ldez/usetesting 3. Linters new features - `gci`: new options: `no-inline-comments`, `no-prefix-comments` - `gomoddirectives`: from 0.2.4 to 0.6.0 (new options: `go-version-pattern`, `toolchain-pattern`,`toolchain-forbidden`, `tool-forbidden`, `go-debug-forbidden`) - `govet`: new `stdversion`, `waitgroup` analyzers - `importas`: allow multiple empty aliases - `loggercheck`: new `slog` option - `recvcheck`: from 0.1.2 to 0.2.0 (new options: `disable-builtin`, `exclusions`) - `tagliatelle`: from 0.5.0 to 0.7.1 (new options: `ignored-fields`, `extended-rules`,`overrides`, `pkg`, `ignore`) - `usestdlibvars`: from 1.27.0 to 1.28.0 (autofix) - `wrapcheck`: from 2.9.0 to 2.10.0 (new option: `extra-ignore-sigs`) 4. Linters bug fixes - `asciicheck`: from 0.2.0 to 0.3.0 - `bodyclose`: from [`5742072`](https://github.com/golangci/golangci-lint/commit/574207250966) to [`ed6a65f`](https://github.com/golangci/golangci-lint/commit/ed6a65f985e) - `funlen`: from 0.1.0 to 0.2.0 - `ginkgolinter`: from 0.18.3 to 0.18.4 - `gochecksumtype`: from 0.2.0 to 0.3.1 - `gocognit`: from 1.1.3 to 1.2.0 - `godot`: from 1.4.18 to 1.4.20 - `goheader`: report position improvement - `gosec`: handling of global nosec option when it is false - `iface`: from 1.2.1 to 1.3.0 - `importas`: from 0.1.0 to 0.2.0 - `intrange`: from 0.2.1 to 0.3.0 - `makezero`: from 1.1.1 to 1.2.0 - `mirror`: from 1.2.0 to 1.3.0 - `nilnil`: from 1.0.0 to 1.0.1 - `nosprintfhostport`: from 0.1.1 to 0.2.0 - `reassign`: from 0.2.0 to 0.3.0 - `spancheck`: from 0.6.2 to 0.6.4 - `tagalign`: from 1.3.4 to 1.4.1 - `wastedassign`: from 2.0.7 to 2.1.0 - `whitespace`: from 0.1.1 to 0.2.0 - `wsl`: from 4.4.1 to 4.5.0 5. Deprecations - ⚠️ `output.uniq-by-line` is deprecated and replaced by `issues.uniq-by-line`. 6. Misc. - Improvements of the help command (color and JSON support). - Removes `decoder`, `sloglint`, `tagalin` from `format` preset. - Enables paths with junction inside Windows. - The timeout is disabled if `run.timeout` <= 0.
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/419 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 8b18851..7e4e94a 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.62.2 + image: golangci/golangci-lint:v1.63.4 commands: - go version - go install mvdan.cc/gofumpt@latest From 7fad16b5d6f85fcad10fbf3041874a7c990cae88 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 17 Jan 2025 19:18:13 +0000 Subject: [PATCH 299/320] chore(deps): update docker.io/woodpeckerci/plugin-prettier docker tag to v1 (#417) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [docker.io/woodpeckerci/plugin-prettier](https://codeberg.org/woodpecker-plugins/prettier) ([source](https://codeberg.org/woodpecker-plugins/prettier.git)) | major | `0.2.0` -> `1.1.0` | --- ### Release Notes
woodpecker-plugins/prettier (docker.io/woodpeckerci/plugin-prettier) ### [`v1.1.0`](https://codeberg.org/woodpecker-plugins/prettier/compare/1.0.0...1.1.0) [Compare Source](https://codeberg.org/woodpecker-plugins/prettier/compare/1.0.0...1.1.0) ### [`v1.0.0`](https://codeberg.org/woodpecker-plugins/prettier/blob/HEAD/CHANGELOG.md#100---2024-11-20) [Compare Source](https://codeberg.org/woodpecker-plugins/prettier/compare/0.2.0...1.0.0) ##### ❤️ Thanks to all contributors! ❤️ [@​qwerty287](https://github.com/qwerty287), [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 💥 Breaking changes - Update node.js to v22 and remove ppc64le and arm images \[[#​2](https://codeberg.org/woodpecker-plugins/prettier/pulls/2)] ##### 📦️ Dependency - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v2.1.1 \[[#​5](https://codeberg.org/woodpecker-plugins/prettier/pulls/5)] ##### Misc - Add CODEOWNERS \[[#​3](https://codeberg.org/woodpecker-plugins/prettier/pulls/3)]
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/417 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 7e4e94a..0d43a8f 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -24,7 +24,7 @@ steps: - yamllint . prettier: - image: docker.io/woodpeckerci/plugin-prettier:0.2.0 + image: docker.io/woodpeckerci/plugin-prettier:1.1.0 depends_on: [] settings: version: 3.2.5 From ead959adf7812675b95144157b440031fe124098 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 17 Jan 2025 19:18:28 +0000 Subject: [PATCH 300/320] fix(deps): update golang.org/x/exp digest to 7588d65 (#418) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | golang.org/x/exp | require | digest | `b2144cd` -> `7588d65` | --- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/418 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- go.mod | 10 +++--- go.sum | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 106 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 13e40df..c329493 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 xorm.io/xorm v1.3.9 ) @@ -207,15 +207,15 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.29.0 // indirect google.golang.org/api v0.214.0 // indirect google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect diff --git a/go.sum b/go.sum index f8ab2be..b765807 100644 --- a/go.sum +++ b/go.sum @@ -605,6 +605,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= @@ -614,14 +615,20 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BL github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 h1:yzrctSl9GMIQ5lHu7jc8olOsGjWDCsBpJhWqfGa/YIM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0/go.mod h1:GE4m0rnnfwLGX0Y9A9A25Zx5N/90jneT5ABevqzhuFQ= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 h1:zLzoX5+W2l95UJoVwiyNS4dX8vHyQ6x2xRLoBBL9wMk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= @@ -638,6 +645,7 @@ github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdE github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= @@ -645,6 +653,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -750,10 +760,13 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -802,12 +815,15 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -835,6 +851,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -846,6 +864,8 @@ github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -894,6 +914,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -906,9 +928,14 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= +github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= @@ -964,6 +991,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -987,6 +1016,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -1015,6 +1046,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= @@ -1068,6 +1101,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= @@ -1085,6 +1119,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -1141,6 +1177,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= @@ -1149,6 +1187,7 @@ github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aW github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -1170,7 +1209,10 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -1189,9 +1231,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= @@ -1246,6 +1290,8 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= @@ -1267,6 +1313,8 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -1319,6 +1367,7 @@ github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZ github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw= github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nzdjb/go-metaname v1.0.0 h1:sNASlZC1RM3nSudtBTE1a3ZVTDyTpjqI5WXRPrdZ9Hg= github.com/nzdjb/go-metaname v1.0.0/go.mod h1:0GR0LshZax1Lz4VrOrfNSE4dGvTp7HGjiemdczXT2H4= @@ -1327,15 +1376,20 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= @@ -1416,8 +1470,11 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE= github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= @@ -1426,6 +1483,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -1466,9 +1525,13 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= +github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= github.com/softlayer/softlayer-go v1.1.7 h1:SgTL+pQZt1h+5QkAhVmHORM/7N9c1X0sljJhuOIHxWE= github.com/softlayer/softlayer-go v1.1.7/go.mod h1:WeJrBLoTJcaT8nO1azeyHyNpo/fDLtbpbvh+pzts+Qw= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= @@ -1546,7 +1609,9 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1 github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550= github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec h1:2s/ghQ8wKE+UzD/hf3P4Gd1j0JI9ncbxv+nsypPoUYI= github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7Qs3ku1ckpqed8tCRSqTlp8NAeZfAVpfx4OzXMss= @@ -1616,6 +1681,10 @@ go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1624,6 +1693,7 @@ go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= @@ -1672,8 +1742,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1689,8 +1759,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= -golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1813,8 +1883,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1981,8 +2051,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1998,6 +2068,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2099,8 +2171,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2392,10 +2464,12 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -2408,6 +2482,7 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24 gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48= gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= @@ -2442,16 +2517,21 @@ k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= @@ -2461,19 +2541,30 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= +modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= +modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= +modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From ebc1444efb14cd7554f572d69baadc6579727c63 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 17 Jan 2025 20:21:32 +0100 Subject: [PATCH 301/320] update go version in docker file --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6106317..7cc21c5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ # Set the default Go version as a build argument -ARG XGO="go-1.21.x" +ARG XGO="go-1.23.x" # Use xgo (a Go cross-compiler tool) as build image -FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} as build +FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} AS build # Set the working directory and copy the source code WORKDIR /go/src/codeberg.org/codeberg/pages From 7ca77716bffbe9f8cb3462ea9dae5e2f77432c75 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 17 Jan 2025 20:21:56 +0100 Subject: [PATCH 302/320] update flake --- flake.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.lock b/flake.lock index 1fdb364..b3837d9 100644 --- a/flake.lock +++ b/flake.lock @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-zUfNgGopg1+/xYRnFaI6PAhlF2JofdEG789CsKclYSw=", - "path": "/nix/store/0ijldwygklvrzinxx7scsxsvqhx0hqgz-source", + "narHash": "sha256-1/WZ7juTZp6oWAFb14WeANN2veJFGZ4AFSNRpk4Jiw8=", + "path": "/nix/store/prhs96gxbb9bcr4ia6ql2hsjga50mrdl-source", "type": "path" }, "original": { From b69d09e9e1cb49ef71628dc860d7324c50b5f832 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 17 Jan 2025 20:29:41 +0100 Subject: [PATCH 303/320] update dependencies --- go.mod | 101 +++++++++++++++++++++++++++-------------------------- go.sum | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 50 deletions(-) diff --git a/go.mod b/go.mod index c329493..1555baa 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,10 @@ module codeberg.org/codeberg/pages go 1.23.0 -toolchain go1.23.3 +toolchain go1.23.5 require ( - code.gitea.io/sdk/gitea v0.19.0 + code.gitea.io/sdk/gitea v0.20.0 github.com/OrlovEvgeny/go-mcache v0.0.0-20200121124330-1a8195b34f3a github.com/creasty/defaults v1.8.0 github.com/go-acme/lego/v4 v4.21.0 @@ -27,14 +27,15 @@ require ( ) require ( - cloud.google.com/go/auth v0.13.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/auth v0.14.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect cloud.google.com/go/compute/metadata v0.6.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/42wim/httpsig v1.2.2 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect @@ -51,40 +52,40 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect - github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 // indirect - github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 // indirect + github.com/aws/aws-sdk-go-v2 v1.33.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.53 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect - github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.10 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.8 // indirect github.com/aws/smithy-go v1.22.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/boombuler/barcode v1.0.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/civo/civogo v0.3.89 // indirect - github.com/cloudflare/cloudflare-go v0.112.0 // indirect + github.com/civo/civogo v0.3.92 // indirect + github.com/cloudflare/cloudflare-go v0.114.0 // indirect github.com/cpu/goacmedns v0.1.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dnsimple/dnsimple-go v1.7.0 // indirect - github.com/exoscale/egoscale/v3 v3.1.7 // indirect + github.com/exoscale/egoscale/v3 v3.1.8 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-fed/httpsig v1.1.0 // indirect @@ -93,8 +94,8 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.23.0 // indirect - github.com/go-resty/resty/v2 v2.16.2 // indirect + github.com/go-playground/validator/v10 v10.24.0 // indirect + github.com/go-resty/resty/v2 v2.16.3 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/gofrs/flock v0.12.1 // indirect @@ -104,7 +105,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/s2a-go v0.1.8 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect @@ -117,7 +118,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129 // indirect + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -128,11 +129,11 @@ require ( github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/goh v1.0.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/linode/linodego v1.44.0 // indirect + github.com/linode/linodego v1.46.0 // indirect github.com/liquidweb/liquidweb-cli v0.7.0 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect github.com/magiconair/properties v1.8.9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/mimuret/golang-iij-dpf v0.9.1 // indirect @@ -153,7 +154,7 @@ require ( github.com/nrdcg/porkbun v0.4.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect - github.com/oracle/oci-go-sdk/v65 v65.81.1 // indirect + github.com/oracle/oci-go-sdk/v65 v65.81.2 // indirect github.com/ovh/go-ovh v1.6.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/peterhellberg/link v1.2.0 // indirect @@ -167,7 +168,7 @@ require ( github.com/sacloud/go-http v0.1.9 // indirect github.com/sacloud/iaas-api-go v1.14.0 // indirect github.com/sacloud/packages-go v0.0.11 // indirect - github.com/sagikazarmark/locafero v0.6.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect github.com/selectel/domains-go v1.1.0 // indirect @@ -179,56 +180,56 @@ require ( github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/sony/gobreaker v1.0.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/transip/gotransip/v6 v6.26.0 // indirect github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect - github.com/volcengine/volc-sdk-golang v1.0.190 // indirect - github.com/vultr/govultr/v3 v3.13.0 // indirect + github.com/volcengine/volc-sdk-golang v1.0.193 // indirect + github.com/vultr/govultr/v3 v3.14.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect - go.mongodb.org/mongo-driver v1.17.1 // indirect + go.mongodb.org/mongo-driver v1.17.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.8.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.29.0 // indirect - google.golang.org/api v0.214.0 // indirect - google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect - google.golang.org/grpc v1.69.2 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/api v0.217.0 // indirect + google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.69.4 // indirect + google.golang.org/protobuf v1.36.3 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.32.0 // indirect - k8s.io/apimachinery v0.32.0 // indirect + k8s.io/api v0.32.1 // indirect + k8s.io/apimachinery v0.32.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect diff --git a/go.sum b/go.sum index b765807..426079f 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ= +cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -99,8 +101,12 @@ cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= +cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= +cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= @@ -175,6 +181,8 @@ cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvj cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= +cloud.google.com/go/compute v1.31.1/go.mod h1:hyOponWhXviDptJCJSoEh89XO1cfv616wbwbkde1/+8= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -600,6 +608,8 @@ cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= +code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= +code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -607,16 +617,23 @@ gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zum git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA= +github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= @@ -689,6 +706,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 h1:mg/+23+/gAw6zdxv9I5dPCj666WJPLk8S1nXm0dOumQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.76/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 h1:YBkf7H5CSgrlb3C1aWcpDt7Vk8UEGFPeD2OOirtt6IM= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.83/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= @@ -705,16 +724,28 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2 v1.33.0 h1:Evgm4DI9imD81V0WwD+TN4DCwjUMdc94TrduMLbgZJs= +github.com/aws/aws-sdk-go-v2 v1.33.0/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= +github.com/aws/aws-sdk-go-v2/config v1.29.0 h1:Vk/u4jof33or1qAQLdofpjKV7mQQT7DcUpnYx8kdmxY= +github.com/aws/aws-sdk-go-v2/config v1.29.0/go.mod h1:iXAZK3Gxvpq3tA+B9WaDYpZis7M8KFgdrDPMmHrgbJM= github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= +github.com/aws/aws-sdk-go-v2/credentials v1.17.53 h1:lwrVhiEDW5yXsuVKlFVUnR2R50zt2DklhOyeLETqDuE= +github.com/aws/aws-sdk-go-v2/credentials v1.17.53/go.mod h1:CkqM1bIw/xjEpBMhBnvqUXYZbpCFuj6dnCAyDk2AtAY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 h1:5grmdTdMsovn9kPZPI23Hhvp0ZyNm5cRO+IZFIYiAfw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24/go.mod h1:zqi7TVKTswH3Ozq28PkmBmgzG1tona7mo9G2IJg4Cis= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 h1:igORFSiH3bfq4lxKFkTSYDhJEUCYo6C8VKiWJjYwQuQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28/go.mod h1:3So8EA/aAYm36L7XIvCVwLa0s5N0P7o2b1oqnx/2R4g= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 h1:1mOW9zAUMhTSrMDssEHS/ajx8JcAj/IcftzcmNlmVLI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28/go.mod h1:kGlXVIWDfvt2Ox5zEaNglmq0hXPHgQFNMix33Tw22jA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= @@ -722,16 +753,28 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhv github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 h1:TQmKDyETFGiXVhZfQ/I0cCFziqqX58pi4tKJGYGFSz0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9/go.mod h1:HVLPK2iHQBUx7HfZeOQSEu3v2ubZaAY2YPbAm5/WUyY= github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 h1:+lmJoqxuUoPlSfGk5JYQQivd9YFjUvRZR6RPY+Wcx48= github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8/go.mod h1:Gg8/myP4+rgRi4+j9gQdbOEnMtwMAUUIeXo+nKCFVj8= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10 h1:D8L0rXdD2L5/DoxkC3sWfBfXbYnabB6F6f4XmqgMQPo= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10/go.mod h1:UIgmS/dicuAwwgEbzEbpyXiPxMW5bU3yvxtZJmNuxFQ= github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 h1:0jMtawybbfpFEIMy4wvfyW2Z4YLr7mnuzT0fhR67Nrc= github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4/go.mod h1:xlMODgumb0Pp8bzfpojqelDrf8SL9rb5ovwmwKJl+oU= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 h1:njgAP7Rtt4DGdTGFPhJ4gaZXCD1CDj/SZDa5W4ZgSTs= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.10 h1:DyZUj3xSw3FR3TXSwDhPhuZkkT14QHBiacdbUVcD0Dg= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.10/go.mod h1:Ro744S4fKiCCuZECXgOi760TiYylUM8ZBf6OGiZzJtY= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9 h1:I1TsPEs34vbpOnR81GIcAq4/3Ud+jRHVGwx6qLQUHLs= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9/go.mod h1:Fzsj6lZEb8AkTE5S68OhcbBqeWPsR8RnGuKPr8Todl8= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.8 h1:pqEJQtlKWvnv3B6VRt60ZmsHy3SotlEBvfUBPB1KVcM= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.8/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= @@ -774,10 +817,14 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/civo/civogo v0.3.89 h1:g+I4NGVa5t0L2Z9+QbnEAqxE/3OCDUYvepje3oUkKVo= github.com/civo/civogo v0.3.89/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= +github.com/civo/civogo v0.3.92 h1:fiA9nsyvEU9vVwA8ssIDSGt6gsWs6meqgaKrimAEaI0= +github.com/civo/civogo v0.3.92/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g= github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0= +github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA= +github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -847,6 +894,8 @@ github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0+ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/exoscale/egoscale/v3 v3.1.7 h1:Q6p9tOVY0IiOW0fUpaPQWY7ggGEuSPZLAGxFgDd2sCE= github.com/exoscale/egoscale/v3 v3.1.7/go.mod h1:GHKucK/J26v8PGWztGdhxWNMjrjG9PbelxKCJ4YI11Q= +github.com/exoscale/egoscale/v3 v3.1.8 h1:LrZ7cAk3Wum9ZncKR1gRPpXsdvCoQb7KoUi3+ZxSTvY= +github.com/exoscale/egoscale/v3 v3.1.8/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -876,6 +925,8 @@ github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o= @@ -922,8 +973,12 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= +github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= +github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E= +github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -1052,6 +1107,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1165,6 +1222,8 @@ github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfE github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129 h1:+4ocCqiQFWTODV2Q94q/DMGrmoXFIrimPn+SZZPCbBs= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 h1:5LqzrJa8LADcY0sDEdV35e8nbwI7RoUQEt+KXWvWoY0= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -1249,6 +1308,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linode/linodego v1.44.0 h1:JZLLWzCAx3CmHSV9NmCoXisuqKtrmPhfY9MrgvaHMUY= github.com/linode/linodego v1.44.0/go.mod h1:umdoNOmtbqAdGQbmQnPFZ2YS4US+/mU/1bA7MjoKAvg= +github.com/linode/linodego v1.46.0 h1:+uOG4SD2MIrhbrLrvOD5HrbdLN3D19Wgn3MgdUNQjeU= +github.com/linode/linodego v1.46.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= @@ -1273,6 +1334,8 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -1397,6 +1460,8 @@ github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mo github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/oracle/oci-go-sdk/v65 v65.81.1 h1:JYc47bk8n/MUchA2KHu1ggsCQzlJZQLJ+tTKfOho00E= github.com/oracle/oci-go-sdk/v65 v65.81.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/oracle/oci-go-sdk/v65 v65.81.2 h1:yhdu9xphYOJed+MPQP4OHAtPlSm0dxKY2L/lf5ntJbU= +github.com/oracle/oci-go-sdk/v65 v65.81.2/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI= github.com/ovh/go-ovh v1.6.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1472,12 +1537,15 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE= github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= +github.com/reugn/equalizer v0.2.0 h1:kJy7909B2SYFLKnpMpfPK//B+IWhx7kEq/N8AUrt27w= +github.com/reugn/equalizer v0.2.0/go.mod h1:Jxi79RdAJ9Ro4nET7fANm0v7TehzmyyAkv2w0TPBSZQ= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1506,6 +1574,8 @@ github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZO github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770= @@ -1552,6 +1622,8 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= @@ -1601,8 +1673,12 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071 h1:Q/Ue/yRv4HSpaiFAnXIshoDjxzwyhwezEidXU49Boa4= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084 h1:kVMffrA57uPQvNaPBXSp1NJPiz+Y0a/bxqiEVBjrZAI= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071 h1:1P0aq3ss/sku5n2qq18qTeyApQVMcy9/b7qbXPejCSc= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071/go.mod h1:wIqmfnhHUG/GMIK4OfsvXA29+Ei4Gmz5K3SWftLVxFQ= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 h1:kwctN0WQYt8/iKP+iRCTCwdzEMIXsXklbRIib5rjeQ8= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084/go.mod h1:qE67ApiBzeRvzeDsV+GxyIDbVIDemsKpHXllQATz/Vw= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1622,8 +1698,12 @@ github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4 github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/volcengine/volc-sdk-golang v1.0.190 h1:bva2jbMuce9LIqWRlfWbZcyriRs17ZaSoLZulMpk6K8= github.com/volcengine/volc-sdk-golang v1.0.190/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= +github.com/volcengine/volc-sdk-golang v1.0.193 h1:mL1rlk+m9SaqF2MSGFWfigEaz10ZVJiYDnFuWfj65Ww= +github.com/volcengine/volc-sdk-golang v1.0.193/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= github.com/vultr/govultr/v3 v3.13.0 h1:Tq26syAju/cTlgorJJX391ZTbhILbe4WzIatyeStXCM= github.com/vultr/govultr/v3 v3.13.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= +github.com/vultr/govultr/v3 v3.14.1 h1:9BpyZgsWasuNoR39YVMcq44MSaF576Z4D+U3ro58eJQ= +github.com/vultr/govultr/v3 v3.14.1/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1665,6 +1745,8 @@ go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQa go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= +go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM= +go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -1679,14 +1761,20 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEj go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -1916,6 +2004,8 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2102,6 +2192,8 @@ golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2249,6 +2341,8 @@ google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45 google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= +google.golang.org/api v0.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU= +google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2391,10 +2485,16 @@ google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 h1:e26eS1K69yxjjNNHYqjN49y95kcaQLJ3TL5h68dcA1E= google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:i5btTErZyoKCCubju3HS5LVho4nZd3yFnEp6moqeUjE= +google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f h1:387Y+JbxF52bmesc8kq1NyYIp33dnxCw6eiA7JMsTmw= +google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:0joYwWwLQh18AOj8zMYeZLjzuqcYTU3/nC5JdCvC3JI= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2439,6 +2539,8 @@ google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3 google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2459,6 +2561,8 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2510,8 +2614,12 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= From cce656ec4e158d0853c01b843dfce7381739392f Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 17 Jan 2025 20:32:11 +0100 Subject: [PATCH 304/320] ignore failure of integration tests --- .woodpecker/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 26ba76f..7a99a42 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -48,6 +48,7 @@ steps: - event: [pull_request] integration-tests: + failure: ignore depends_on: build image: codeberg.org/6543/docker-images/golang_just:go-1.23 commands: From a77e2d944065bfdb25bff9353bdb029db37e81c8 Mon Sep 17 00:00:00 2001 From: crapStone Date: Fri, 17 Jan 2025 19:49:16 +0000 Subject: [PATCH 305/320] fix: timeout issue of gitea sdk client (#422) closes #420 Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/422 Co-authored-by: crapStone Co-committed-by: crapStone --- server/gitea/client.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/server/gitea/client.go b/server/gitea/client.go index 0cf6e6c..5633bf2 100644 --- a/server/gitea/client.go +++ b/server/gitea/client.go @@ -49,6 +49,7 @@ const ( type Client struct { sdkClient *gitea.Client + sdkFileClient *gitea.Client responseCache cache.ICache giteaRoot string @@ -68,8 +69,6 @@ func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error) } giteaRoot := strings.TrimSuffix(rootURL.String(), "/") - stdClient := http.Client{Timeout: 10 * time.Second} - forbiddenMimeTypes := make(map[string]bool, len(cfg.ForbiddenMimeTypes)) for _, mimeType := range cfg.ForbiddenMimeTypes { forbiddenMimeTypes[mimeType] = true @@ -80,15 +79,26 @@ func NewClient(cfg config.ForgeConfig, respCache cache.ICache) (*Client, error) defaultMimeType = "application/octet-stream" } - sdk, err := gitea.NewClient( + sdkClient, err := gitea.NewClient( giteaRoot, - gitea.SetHTTPClient(&stdClient), + gitea.SetHTTPClient(&http.Client{Timeout: 10 * time.Second}), + gitea.SetToken(cfg.Token), + gitea.SetUserAgent("pages-server/"+version.Version), + ) + if err != nil { + return nil, err + } + + sdkFileClient, err := gitea.NewClient( + giteaRoot, + gitea.SetHTTPClient(&http.Client{Timeout: 1 * time.Hour}), gitea.SetToken(cfg.Token), gitea.SetUserAgent("pages-server/"+version.Version), ) return &Client{ - sdkClient: sdk, + sdkClient: sdkClient, + sdkFileClient: sdkFileClient, responseCache: respCache, giteaRoot: giteaRoot, @@ -154,7 +164,7 @@ func (client *Client) ServeRawContent(ctx *context.Context, targetOwner, targetR } log.Trace().Msg("file not in cache") // not in cache, open reader via gitea api - reader, resp, err := client.sdkClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS) + reader, resp, err := client.sdkFileClient.GetFileReader(targetOwner, targetRepo, ref, resource, client.supportLFS) if resp != nil { switch resp.StatusCode { case http.StatusOK: From 9450415545d945e3e2061b60cb3da5f79f6f305d Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 21 Jan 2025 00:25:00 +0000 Subject: [PATCH 306/320] chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.3 (#424) --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 7a99a42..2860f81 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -63,7 +63,7 @@ steps: release: depends_on: build - image: woodpeckerci/plugin-release:0.2.2 + image: woodpeckerci/plugin-release:0.2.3 settings: base_url: https://codeberg.org file_exists: overwrite From 0b7687684a888f23dda4ff3d445eb4b05201f1db Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 7 Feb 2025 10:33:44 +0000 Subject: [PATCH 307/320] chore(deps): update mstruebing/editorconfig-checker docker tag to v3.1.2 (#423) --- .woodpecker/lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index 0d43a8f..f2dee1f 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -11,11 +11,11 @@ steps: - go version - go install mvdan.cc/gofumpt@latest - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }" - - golangci-lint run --timeout 5m --build-tags integration + - golangci-lint run --timeout 10m --build-tags integration editor-config: depends_on: [] - image: mstruebing/editorconfig-checker:v3.0.3 + image: mstruebing/editorconfig-checker:v3.2.0 yamllint: image: pipelinecomponents/yamllint:0.33.0 From a761f1284978f57082670406096f4959d7849c89 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 7 Feb 2025 11:00:09 +0000 Subject: [PATCH 308/320] chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.0 (#426) --- .woodpecker/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 2860f81..2b8b7eb 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -20,7 +20,7 @@ steps: docker-dryrun: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.1.0 + image: woodpeckerci/plugin-docker-buildx:5.2.1 settings: dockerfile: Dockerfile platforms: linux/amd64 @@ -75,7 +75,7 @@ steps: docker-next: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.1.0 + image: woodpeckerci/plugin-docker-buildx:5.2.1 settings: registry: codeberg.org dockerfile: Dockerfile @@ -90,7 +90,7 @@ steps: - event: [push] 'Publish PR image': - image: woodpeckerci/plugin-docker-buildx:5.1.0 + image: woodpeckerci/plugin-docker-buildx:5.2.1 depends_on: test settings: registry: codeberg.org @@ -108,7 +108,7 @@ steps: docker-release: depends_on: vendor - image: woodpeckerci/plugin-docker-buildx:5.1.0 + image: woodpeckerci/plugin-docker-buildx:5.2.1 settings: registry: codeberg.org dockerfile: Dockerfile From 7f7ef63e842173548fcf0fe011700b43a8127b39 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sat, 8 Feb 2025 09:52:19 +0000 Subject: [PATCH 309/320] fix(deps): update golang.org/x/exp digest to e0ece0d (#427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | golang.org/x/exp | require | digest | `7588d65` -> `e0ece0d` | --- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Co-authored-by: Patrick Schratz Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/427 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- go.mod | 2 +- go.sum | 113 ++------------------------------------------------------- 2 files changed, 5 insertions(+), 110 deletions(-) diff --git a/go.mod b/go.mod index 1555baa..9442210 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 + golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c xorm.io/xorm v1.3.9 ) diff --git a/go.sum b/go.sum index 426079f..b3f83de 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,6 @@ cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRY cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ= -cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM= cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= @@ -99,12 +97,8 @@ cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVo cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= -cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= @@ -181,8 +175,6 @@ cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvj cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.31.1 h1:SObuy8Fs6woazArpXp1fsHCw+ZH4iJ/8dGGTxUhHZQA= -cloud.google.com/go/compute v1.31.1/go.mod h1:hyOponWhXviDptJCJSoEh89XO1cfv616wbwbkde1/+8= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= @@ -606,8 +598,6 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y= -code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI= code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU= code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -623,17 +613,12 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYs github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= @@ -704,8 +689,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.76 h1:mg/+23+/gAw6zdxv9I5dPCj666WJPLk8S1nXm0dOumQ= -github.com/aliyun/alibaba-cloud-sdk-go v1.63.76/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/aliyun/alibaba-cloud-sdk-go v1.63.83 h1:YBkf7H5CSgrlb3C1aWcpDt7Vk8UEGFPeD2OOirtt6IM= github.com/aliyun/alibaba-cloud-sdk-go v1.63.83/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -722,28 +705,16 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw= -github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2 v1.33.0 h1:Evgm4DI9imD81V0WwD+TN4DCwjUMdc94TrduMLbgZJs= github.com/aws/aws-sdk-go-v2 v1.33.0/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE= -github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M= github.com/aws/aws-sdk-go-v2/config v1.29.0 h1:Vk/u4jof33or1qAQLdofpjKV7mQQT7DcUpnYx8kdmxY= github.com/aws/aws-sdk-go-v2/config v1.29.0/go.mod h1:iXAZK3Gxvpq3tA+B9WaDYpZis7M8KFgdrDPMmHrgbJM= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs= github.com/aws/aws-sdk-go-v2/credentials v1.17.53 h1:lwrVhiEDW5yXsuVKlFVUnR2R50zt2DklhOyeLETqDuE= github.com/aws/aws-sdk-go-v2/credentials v1.17.53/go.mod h1:CkqM1bIw/xjEpBMhBnvqUXYZbpCFuj6dnCAyDk2AtAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 h1:5grmdTdMsovn9kPZPI23Hhvp0ZyNm5cRO+IZFIYiAfw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24/go.mod h1:zqi7TVKTswH3Ozq28PkmBmgzG1tona7mo9G2IJg4Cis= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 h1:igORFSiH3bfq4lxKFkTSYDhJEUCYo6C8VKiWJjYwQuQ= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28/go.mod h1:3So8EA/aAYm36L7XIvCVwLa0s5N0P7o2b1oqnx/2R4g= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 h1:1mOW9zAUMhTSrMDssEHS/ajx8JcAj/IcftzcmNlmVLI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28/go.mod h1:kGlXVIWDfvt2Ox5zEaNglmq0hXPHgQFNMix33Tw22jA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= @@ -751,28 +722,16 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJN github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 h1:TQmKDyETFGiXVhZfQ/I0cCFziqqX58pi4tKJGYGFSz0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9/go.mod h1:HVLPK2iHQBUx7HfZeOQSEu3v2ubZaAY2YPbAm5/WUyY= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 h1:+lmJoqxuUoPlSfGk5JYQQivd9YFjUvRZR6RPY+Wcx48= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8/go.mod h1:Gg8/myP4+rgRi4+j9gQdbOEnMtwMAUUIeXo+nKCFVj8= github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10 h1:D8L0rXdD2L5/DoxkC3sWfBfXbYnabB6F6f4XmqgMQPo= github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.10/go.mod h1:UIgmS/dicuAwwgEbzEbpyXiPxMW5bU3yvxtZJmNuxFQ= -github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 h1:0jMtawybbfpFEIMy4wvfyW2Z4YLr7mnuzT0fhR67Nrc= -github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4/go.mod h1:xlMODgumb0Pp8bzfpojqelDrf8SL9rb5ovwmwKJl+oU= github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1 h1:njgAP7Rtt4DGdTGFPhJ4gaZXCD1CDj/SZDa5W4ZgSTs= github.com/aws/aws-sdk-go-v2/service/route53 v1.48.1/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg= github.com/aws/aws-sdk-go-v2/service/sso v1.24.10 h1:DyZUj3xSw3FR3TXSwDhPhuZkkT14QHBiacdbUVcD0Dg= github.com/aws/aws-sdk-go-v2/service/sso v1.24.10/go.mod h1:Ro744S4fKiCCuZECXgOi760TiYylUM8ZBf6OGiZzJtY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9 h1:I1TsPEs34vbpOnR81GIcAq4/3Ud+jRHVGwx6qLQUHLs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.9/go.mod h1:Fzsj6lZEb8AkTE5S68OhcbBqeWPsR8RnGuKPr8Todl8= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.8 h1:pqEJQtlKWvnv3B6VRt60ZmsHy3SotlEBvfUBPB1KVcM= github.com/aws/aws-sdk-go-v2/service/sts v1.33.8/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= @@ -815,14 +774,10 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/civo/civogo v0.3.89 h1:g+I4NGVa5t0L2Z9+QbnEAqxE/3OCDUYvepje3oUkKVo= -github.com/civo/civogo v0.3.89/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= github.com/civo/civogo v0.3.92 h1:fiA9nsyvEU9vVwA8ssIDSGt6gsWs6meqgaKrimAEaI0= github.com/civo/civogo v0.3.92/go.mod h1:7UCYX+qeeJbrG55E1huv+0ySxcHTqq/26FcHLVelQJM= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g= -github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0= github.com/cloudflare/cloudflare-go v0.114.0 h1:ucoti4/7Exo0XQ+rzpn1H+IfVVe++zgiM+tyKtf0HUA= github.com/cloudflare/cloudflare-go v0.114.0/go.mod h1:O7fYfFfA6wKqKFn2QIR9lhj7FDw6VQCGOY6hd2TBtd0= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -892,8 +847,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/exoscale/egoscale/v3 v3.1.7 h1:Q6p9tOVY0IiOW0fUpaPQWY7ggGEuSPZLAGxFgDd2sCE= -github.com/exoscale/egoscale/v3 v3.1.7/go.mod h1:GHKucK/J26v8PGWztGdhxWNMjrjG9PbelxKCJ4YI11Q= github.com/exoscale/egoscale/v3 v3.1.8 h1:LrZ7cAk3Wum9ZncKR1gRPpXsdvCoQb7KoUi3+ZxSTvY= github.com/exoscale/egoscale/v3 v3.1.8/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -923,8 +876,6 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= -github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -971,12 +922,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg= github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= -github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg= -github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU= github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E= github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -1105,8 +1052,6 @@ github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgY github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1220,8 +1165,6 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129 h1:+4ocCqiQFWTODV2Q94q/DMGrmoXFIrimPn+SZZPCbBs= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.129/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132 h1:5LqzrJa8LADcY0sDEdV35e8nbwI7RoUQEt+KXWvWoY0= github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.132/go.mod h1:JWz2ujO9X3oU5wb6kXp+DpR2UuDj2SldDbX8T0FSuhI= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= @@ -1306,8 +1249,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/linode/linodego v1.44.0 h1:JZLLWzCAx3CmHSV9NmCoXisuqKtrmPhfY9MrgvaHMUY= -github.com/linode/linodego v1.44.0/go.mod h1:umdoNOmtbqAdGQbmQnPFZ2YS4US+/mU/1bA7MjoKAvg= github.com/linode/linodego v1.46.0 h1:+uOG4SD2MIrhbrLrvOD5HrbdLN3D19Wgn3MgdUNQjeU= github.com/linode/linodego v1.46.0/go.mod h1:vyklQRzZUWhFVBZdYx4dcYJU/gG9yKB9VUcUs6ub0Lk= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= @@ -1332,7 +1273,6 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -1458,8 +1398,6 @@ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYr github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/oracle/oci-go-sdk/v65 v65.81.1 h1:JYc47bk8n/MUchA2KHu1ggsCQzlJZQLJ+tTKfOho00E= -github.com/oracle/oci-go-sdk/v65 v65.81.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/oracle/oci-go-sdk/v65 v65.81.2 h1:yhdu9xphYOJed+MPQP4OHAtPlSm0dxKY2L/lf5ntJbU= github.com/oracle/oci-go-sdk/v65 v65.81.2/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= github.com/ovh/go-ovh v1.6.0 h1:ixLOwxQdzYDx296sXcgS35TOPEahJkpjMGtzPadCjQI= @@ -1535,17 +1473,14 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE= github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad h1:WtSUHi5zthjudjIi3L6QmL/V9vpJPbc/j/F2u55d3fs= github.com/reugn/equalizer v0.0.0-20210216135016-a959c509d7ad/go.mod h1:h0+DiDRe2Y+6iHTjIq/9HzUq7NII/Nffp0HkFrsAKq4= -github.com/reugn/equalizer v0.2.0 h1:kJy7909B2SYFLKnpMpfPK//B+IWhx7kEq/N8AUrt27w= -github.com/reugn/equalizer v0.2.0/go.mod h1:Jxi79RdAJ9Ro4nET7fANm0v7TehzmyyAkv2w0TPBSZQ= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -1572,8 +1507,6 @@ github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFn github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= -github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= -github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -1620,8 +1553,6 @@ github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -1671,12 +1602,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071 h1:Q/Ue/yRv4HSpaiFAnXIshoDjxzwyhwezEidXU49Boa4= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1071/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084 h1:kVMffrA57uPQvNaPBXSp1NJPiz+Y0a/bxqiEVBjrZAI= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1084/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071 h1:1P0aq3ss/sku5n2qq18qTeyApQVMcy9/b7qbXPejCSc= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1071/go.mod h1:wIqmfnhHUG/GMIK4OfsvXA29+Ei4Gmz5K3SWftLVxFQ= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084 h1:kwctN0WQYt8/iKP+iRCTCwdzEMIXsXklbRIib5rjeQ8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1084/go.mod h1:qE67ApiBzeRvzeDsV+GxyIDbVIDemsKpHXllQATz/Vw= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= @@ -1696,12 +1623,8 @@ github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= -github.com/volcengine/volc-sdk-golang v1.0.190 h1:bva2jbMuce9LIqWRlfWbZcyriRs17ZaSoLZulMpk6K8= -github.com/volcengine/volc-sdk-golang v1.0.190/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= github.com/volcengine/volc-sdk-golang v1.0.193 h1:mL1rlk+m9SaqF2MSGFWfigEaz10ZVJiYDnFuWfj65Ww= github.com/volcengine/volc-sdk-golang v1.0.193/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE= -github.com/vultr/govultr/v3 v3.13.0 h1:Tq26syAju/cTlgorJJX391ZTbhILbe4WzIatyeStXCM= -github.com/vultr/govultr/v3 v3.13.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/vultr/govultr/v3 v3.14.1 h1:9BpyZgsWasuNoR39YVMcq44MSaF576Z4D+U3ro58eJQ= github.com/vultr/govultr/v3 v3.14.1/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -1743,8 +1666,6 @@ go.etcd.io/etcd/client/v2 v2.305.7/go.mod h1:GQGT5Z3TBuAQGvgPfhR7VPySu/SudxmEkRq go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= -go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= -go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM= go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1759,20 +1680,14 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= @@ -1847,8 +1762,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= -golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= +golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -2002,8 +1917,6 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2190,8 +2103,6 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2339,8 +2250,6 @@ google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60c google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms= -google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= -google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= google.golang.org/api v0.217.0 h1:GYrUtD289o4zl1AhiTZL0jvQGa2RDLyC+kX1N/lfGOU= google.golang.org/api v0.217.0/go.mod h1:qMc2E8cBAbQlRypBTBWHklNJlaZZJBwDv81B1Iu8oSI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -2483,16 +2392,10 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 h1:e26eS1K69yxjjNNHYqjN49y95kcaQLJ3TL5h68dcA1E= -google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:i5btTErZyoKCCubju3HS5LVho4nZd3yFnEp6moqeUjE= google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f h1:387Y+JbxF52bmesc8kq1NyYIp33dnxCw6eiA7JMsTmw= google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:0joYwWwLQh18AOj8zMYeZLjzuqcYTU3/nC5JdCvC3JI= -google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0= -google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -2537,8 +2440,6 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= -google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -2559,8 +2460,6 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -2612,12 +2511,8 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= -k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= -k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= -k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= From 91c4577a43de01451ba314b4725fbf2acd084105 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sat, 15 Feb 2025 00:05:42 +0000 Subject: [PATCH 310/320] fix(deps): update golang.org/x/exp digest to 939b2ce (#430) --- go.mod | 16 ++++++++-------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 9442210..5df680c 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac xorm.io/xorm v1.3.9 ) @@ -208,15 +208,15 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.34.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/net v0.35.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.29.0 // indirect + golang.org/x/tools v0.30.0 // indirect google.golang.org/api v0.217.0 // indirect google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect diff --git a/go.sum b/go.sum index b3f83de..6ba2d51 100644 --- a/go.sum +++ b/go.sum @@ -1745,8 +1745,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1762,8 +1762,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= -golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1806,8 +1806,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1886,8 +1886,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1935,8 +1935,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2054,8 +2054,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2071,8 +2071,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2093,8 +2093,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2174,8 +2174,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= -golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c7fddf202db8b136076d043ff37d519edc7a6033 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 15 Feb 2025 19:08:08 +0000 Subject: [PATCH 311/320] chore: update golang (#431) ping #428 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/431 Co-authored-by: crapStone Co-committed-by: crapStone --- .woodpecker/build.yml | 10 +++++----- .woodpecker/lint.yml | 2 +- Dockerfile | 2 +- flake.lock | 4 ++-- go.mod | 4 +--- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 2b8b7eb..20e47eb 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -5,13 +5,13 @@ when: steps: # use vendor to cache dependencies vendor: - image: golang:1.23 + image: golang:1.24 commands: - go mod vendor build: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just:go-1.23 + image: codeberg.org/6543/docker-images/golang_just:go-1.24 commands: - go version - just build @@ -32,7 +32,7 @@ steps: build-tag: depends_on: vendor - image: codeberg.org/6543/docker-images/golang_just:go-1.23 + image: codeberg.org/6543/docker-images/golang_just:go-1.24 commands: - go version - just build-tag ${CI_COMMIT_TAG##v} @@ -41,7 +41,7 @@ steps: test: depends_on: build - image: codeberg.org/6543/docker-images/golang_just:go-1.23 + image: codeberg.org/6543/docker-images/golang_just:go-1.24 commands: - just test when: @@ -50,7 +50,7 @@ steps: integration-tests: failure: ignore depends_on: build - image: codeberg.org/6543/docker-images/golang_just:go-1.23 + image: codeberg.org/6543/docker-images/golang_just:go-1.24 commands: - just integration environment: diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index f2dee1f..bdb6fb0 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.63.4 + image: golangci/golangci-lint:v1.64.5 commands: - go version - go install mvdan.cc/gofumpt@latest diff --git a/Dockerfile b/Dockerfile index 7cc21c5..dfec6d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Set the default Go version as a build argument -ARG XGO="go-1.23.x" +ARG XGO="go-1.24.x" # Use xgo (a Go cross-compiler tool) as build image FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} AS build diff --git a/flake.lock b/flake.lock index b3837d9..ef63308 100644 --- a/flake.lock +++ b/flake.lock @@ -20,8 +20,8 @@ "nixpkgs": { "locked": { "lastModified": 0, - "narHash": "sha256-1/WZ7juTZp6oWAFb14WeANN2veJFGZ4AFSNRpk4Jiw8=", - "path": "/nix/store/prhs96gxbb9bcr4ia6ql2hsjga50mrdl-source", + "narHash": "sha256-WFZDy4bG2RkkCQloIEG8BXEvzyKklFVJbAismOJsIp4=", + "path": "/nix/store/c77dsgfxjywplw8bk8s8jlkdsr7a1bi9-source", "type": "path" }, "original": { diff --git a/go.mod b/go.mod index 5df680c..bff6b77 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module codeberg.org/codeberg/pages -go 1.23.0 - -toolchain go1.23.5 +go 1.24.0 require ( code.gitea.io/sdk/gitea v0.20.0 From a597e40a556a2e6fa69f1942526725f7c7dda2f8 Mon Sep 17 00:00:00 2001 From: crapStone Date: Sat, 15 Feb 2025 19:32:29 +0000 Subject: [PATCH 312/320] setup direnv (#433) Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/433 Co-authored-by: crapStone Co-committed-by: crapStone --- .envrc | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c4b17d7 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use_flake diff --git a/.gitignore b/.gitignore index 66e7084..fb8d483 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ pages certs.sqlite .bash_history pkg/ +.direnv/ From f123d226a1efc397a54b98c66aedf8d8a305b2e2 Mon Sep 17 00:00:00 2001 From: crapStone Date: Wed, 19 Feb 2025 23:20:06 +0100 Subject: [PATCH 313/320] fix: build docker on release --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 20e47eb..37e2dfe 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -16,7 +16,7 @@ steps: - go version - just build when: - - event: [push, pull_request] + - event: [push, pull_request, tag] docker-dryrun: depends_on: vendor From 4b1faa5ebb678d544f887632c1e8e1624a3bcd9e Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Sun, 2 Mar 2025 00:14:55 +0000 Subject: [PATCH 314/320] fix(deps): update golang.org/x/exp digest to dead583 (#436) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bff6b77..38ffc0f 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac + golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 xorm.io/xorm v1.3.9 ) diff --git a/go.sum b/go.sum index 6ba2d51..1364eed 100644 --- a/go.sum +++ b/go.sum @@ -1762,8 +1762,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= -golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c= +golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= From 4efb5a7f3b243478c92f93b2339b3f06ae830d81 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Wed, 12 Mar 2025 17:06:57 +0000 Subject: [PATCH 315/320] chore(deps): update golangci/golangci-lint docker tag to v1.64.7 (#439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [golangci/golangci-lint](https://github.com/golangci/golangci-lint) | patch | `v1.64.5` -> `v1.64.7` | --- ### Release Notes
golangci/golangci-lint (golangci/golangci-lint) ### [`v1.64.7`](https://github.com/golangci/golangci-lint/releases/tag/v1.64.7) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.6...v1.64.7) `golangci-lint` is a free and open-source project built by volunteers. If you value it, consider supporting us, the [maintainers](https://opencollective.com/golangci-lint) and [linter authors](https://golangci-lint.run/product/thanks/). We appreciate it! :heart: For key updates, see the [changelog](https://golangci-lint.run/product/changelog/#​1647). #### Changelog - [`94946f3`](https://github.com/golangci/golangci-lint/commit/94946f3c52a89774b2c7d255311c34f17f8fac7a) build(deps): bump github.com/OpenPeeDeeP/depguard/v2 from 2.2.0 to 2.2.1 ([#​5509](https://github.com/golangci/golangci-lint/issues/5509)) - [`132365e`](https://github.com/golangci/golangci-lint/commit/132365e252c985a191c6eaea3f0cc01ca9120ccc) build(deps): bump github.com/golangci/dupl from [`3e9179a`](https://github.com/golangci/golangci-lint/commit/3e9179ac440a) to [`f665c8d`](https://github.com/golangci/golangci-lint/commit/f665c8d69b32) ([#​5512](https://github.com/golangci/golangci-lint/issues/5512)) - [`bddd1bc`](https://github.com/golangci/golangci-lint/commit/bddd1bcedbc2f3d767e2362be91ff9eb481493cd) build(deps): bump github.com/securego/gosec/v2 from 2.22.1 to 2.22.2 ([#​5515](https://github.com/golangci/golangci-lint/issues/5515)) - [`624fb4e`](https://github.com/golangci/golangci-lint/commit/624fb4e717dc89f1b55d6a84a69355fe64d60036) build(deps): bump golang.org/x/mod from 0.23.0 to 0.24.0 ([#​5507](https://github.com/golangci/golangci-lint/issues/5507)) - [`8cffdb7`](https://github.com/golangci/golangci-lint/commit/8cffdb7d21e5b2b89f163f70c60ac9686c9d6180) build(deps): bump golang.org/x/oauth2 from 0.27.0 to 0.28.0 in /scripts/gen_github_action_config in the scripts group ([#​5521](https://github.com/golangci/golangci-lint/issues/5521)) - [`7a3f3d7`](https://github.com/golangci/golangci-lint/commit/7a3f3d7c941475ee01edca221c44ea5ba59290a3) build(deps): bump golang.org/x/tools from 0.30.0 to 0.31.0 ([#​5508](https://github.com/golangci/golangci-lint/issues/5508)) - [`c13fd5b`](https://github.com/golangci/golangci-lint/commit/c13fd5b7627c436246f36044a575990b5ec75c7d) build(deps): bump honnef.co/go/tools from 0.6.0 to 0.6.1 ([#​5510](https://github.com/golangci/golangci-lint/issues/5510)) ### [`v1.64.6`](https://github.com/golangci/golangci-lint/blob/HEAD/CHANGELOG.md#v1646) [Compare Source](https://github.com/golangci/golangci-lint/compare/v1.64.5...v1.64.6) 1. Linters bug fixes - `asciicheck`: from 0.4.0 to 0.4.1 - `contextcheck`: from 1.1.5 to 1.1.6 - `errcheck`: from 1.8.0 to 1.9.0 - `exptostd`: from 0.4.1 to 0.4.2 - `ginkgolinter`: from 0.19.0 to 0.19.1 - `go-exhaustruct`: from 3.3.0 to 3.3.1 - `gocheckcompilerdirectives`: from 1.2.1 to 1.3.0 - `godot`: from 1.4.20 to 1.5.0 - `perfsprint`: from 0.8.1 to 0.8.2 - `revive`: from 1.6.1 to 1.7.0 - `tagalign`: from 1.4.1 to 1.4.2
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/439 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index bdb6fb0..d7cf141 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.64.5 + image: golangci/golangci-lint:v1.64.7 commands: - go version - go install mvdan.cc/gofumpt@latest From e2395a057405a10285bfa583dc9c968a7d99b254 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Wed, 12 Mar 2025 22:18:46 +0000 Subject: [PATCH 316/320] fix(deps): update golang.org/x/exp digest to 054e65f (#438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | golang.org/x/exp | require | digest | `dead583` -> `054e65f` | --- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/438 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- go.mod | 16 ++++++++-------- go.sum | 36 ++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/go.mod b/go.mod index 38ffc0f..d5c8e24 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/rs/zerolog v1.33.0 github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 - golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 xorm.io/xorm v1.3.9 ) @@ -206,15 +206,15 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.35.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.37.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.30.0 // indirect + golang.org/x/tools v0.31.0 // indirect google.golang.org/api v0.217.0 // indirect google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect diff --git a/go.sum b/go.sum index 1364eed..9e78abb 100644 --- a/go.sum +++ b/go.sum @@ -1745,8 +1745,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1762,8 +1762,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1806,8 +1806,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1886,8 +1886,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1935,8 +1935,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2054,8 +2054,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2071,8 +2071,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2093,8 +2093,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2174,8 +2174,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From a0d202ef55427cd063e2526384046a0c9374c704 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Wed, 12 Mar 2025 22:19:23 +0000 Subject: [PATCH 317/320] chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.4 (#434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [woodpeckerci/plugin-release](https://codeberg.org/woodpecker-plugins/release) ([source](https://codeberg.org/woodpecker-plugins/release.git)) | patch | `0.2.3` -> `0.2.4` | --- ### Release Notes
woodpecker-plugins/release (woodpeckerci/plugin-release) ### [`v0.2.4`](https://codeberg.org/woodpecker-plugins/release/blob/HEAD/CHANGELOG.md#024---2025-02-14) [Compare Source](https://codeberg.org/woodpecker-plugins/release/compare/v0.2.3...v0.2.4) ##### ❤️ Thanks to all contributors! ❤️ [@​qwerty287](https://github.com/qwerty287), [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 🐛 Bug Fixes - Fix attachment fetching \[[#​33](https://codeberg.org/woodpecker-plugins/release/pulls/33)] ##### 📦️ Dependency - fix(deps): update module github.com/google/go-github/v68 to v69 \[[#​30](https://codeberg.org/woodpecker-plugins/release/pulls/30)] - chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.3 \[[#​28](https://codeberg.org/woodpecker-plugins/release/pulls/28)]
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/434 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 37e2dfe..7a1f957 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -63,7 +63,7 @@ steps: release: depends_on: build - image: woodpeckerci/plugin-release:0.2.3 + image: woodpeckerci/plugin-release:0.2.4 settings: base_url: https://codeberg.org file_exists: overwrite From 4672cdb54d437a1b2c9881f12b95204b8720094f Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Wed, 12 Mar 2025 23:16:56 +0000 Subject: [PATCH 318/320] chore(deps): update docker.io/woodpeckerci/plugin-prettier docker tag to v1.3.0 (#435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | [docker.io/woodpeckerci/plugin-prettier](https://codeberg.org/woodpecker-plugins/prettier) ([source](https://codeberg.org/woodpecker-plugins/prettier.git)) | minor | `1.1.0` -> `1.3.0` | --- ### Release Notes
woodpecker-plugins/prettier (docker.io/woodpeckerci/plugin-prettier) ### [`v1.3.0`](https://codeberg.org/woodpecker-plugins/prettier/blob/HEAD/CHANGELOG.md#130---2025-02-18) [Compare Source](https://codeberg.org/woodpecker-plugins/prettier/compare/1.2.1...1.3.0) ##### ❤️ Thanks to all contributors! ❤️ [@​OCram85](https://github.com/OCram85) ##### 📈 Enhancement - adds output parameter for prettier results \[[#​25](https://codeberg.org/woodpecker-plugins/prettier/pulls/25)] ### [`v1.2.1`](https://codeberg.org/woodpecker-plugins/prettier/blob/HEAD/CHANGELOG.md#121---2025-02-13) [Compare Source](https://codeberg.org/woodpecker-plugins/prettier/compare/1.1.0...1.2.1) ##### ❤️ Thanks to all contributors! ❤️ [@​woodpecker-bot](https://github.com/woodpecker-bot) ##### 📦️ Dependency - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.1 \[[#​24](https://codeberg.org/woodpecker-plugins/prettier/pulls/24)] - chore(deps): update woodpeckerci/plugin-docker-buildx docker tag to v5.2.0 \[[#​22](https://codeberg.org/woodpecker-plugins/prettier/pulls/22)] - chore(deps): update woodpeckerci/plugin-ready-release-go docker tag to v3.1.3 \[[#​21](https://codeberg.org/woodpecker-plugins/prettier/pulls/21)]
--- ### Configuration 📅 **Schedule**: Branch creation - "* * * * 0,6" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: woodpecker-bot Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/435 Co-authored-by: Dependency bot Co-committed-by: Dependency bot --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index d7cf141..ffa57c3 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -24,7 +24,7 @@ steps: - yamllint . prettier: - image: docker.io/woodpeckerci/plugin-prettier:1.1.0 + image: docker.io/woodpeckerci/plugin-prettier:1.3.0 depends_on: [] settings: version: 3.2.5 From 0e873d97836b676a824f2ae631ad6ab7d5d92ce3 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Tue, 25 Mar 2025 00:04:28 +0000 Subject: [PATCH 319/320] chore(deps): update golangci/golangci-lint docker tag to v1.64.8 (#447) --- .woodpecker/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml index ffa57c3..f973eb8 100644 --- a/.woodpecker/lint.yml +++ b/.woodpecker/lint.yml @@ -6,7 +6,7 @@ when: steps: lint: depends_on: [] - image: golangci/golangci-lint:v1.64.7 + image: golangci/golangci-lint:v1.64.8 commands: - go version - go install mvdan.cc/gofumpt@latest From d7deecf5f9202d559fb830eb0fdf412119dc7273 Mon Sep 17 00:00:00 2001 From: Dependency bot Date: Fri, 28 Mar 2025 00:04:20 +0000 Subject: [PATCH 320/320] chore(deps): update woodpeckerci/plugin-release docker tag to v0.2.5 (#449) --- .woodpecker/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 7a1f957..a872106 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -63,7 +63,7 @@ steps: release: depends_on: build - image: woodpeckerci/plugin-release:0.2.4 + image: woodpeckerci/plugin-release:0.2.5 settings: base_url: https://codeberg.org file_exists: overwrite