Skip to content

Commit cc9b7ae

Browse files
committed
Add media mirroring
1 parent bd70668 commit cc9b7ae

12 files changed

Lines changed: 226 additions & 74 deletions

File tree

_config.i2p.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p
7+
- http://codonfttnvztewul3qgathdxjr2fnv34udrqyc6quw3zptqojnea.b32.i2p
78
nostr:
89
spam_api_overwrite: http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/spam.nostr.band/spam_api
910
profile_relays_overwrite:

_config.tor.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion
7+
- http://codonafte3ygy3szn5qwsdje4vp6mwobvzd75jl4ytyrb262cpkaegid.onion
78
nostr:
89
spam_api_overwrite: http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr/spam.nostr.band/spam_api
910
profile_relays_overwrite:

_config.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ defaults:
3535
previous_page: Prev
3636
javascript_required_comments: Please enable JavaScript to view comments.
3737
p2p_player_warning: Too slow? Try
38+
onion_mirror: onion mirror
39+
i2p_mirror: i2p mirror
40+
or: or
3841
leave_comment_on_upstream: Leave comment using
3942
- scope:
4043
path: 'ru/*'
@@ -59,6 +62,9 @@ defaults:
5962
previous_page: Назад
6063
javascript_required_comments: Please enable JavaScript to view comments.
6164
p2p_player_warning: Медленно грузится? Попробуй
65+
onion_mirror: зеркало
66+
i2p_mirror: другое зеркало
67+
or: или
6268
leave_comment_on_upstream: Оставить коммент через
6369

6470
theme_settings:
@@ -80,6 +86,7 @@ theme_settings:
8086
flickr:
8187
gitlab:
8288
github: codonaft
89+
i2p: http://codonftbnpdkjwyflssto3iklawhuthbe37l6swigegqkyyfmiqa.b32.i2p
8390
instagram:
8491
telegram_ru: codonaft_official
8592
keybase:
@@ -160,7 +167,7 @@ theme_settings:
160167
post_navigation: true
161168

162169
p2p_player:
163-
swarmPrefix: https://media.codonaft.com
170+
swarm_prefix: https://media.codonaft.com
164171
media_urls:
165172
- https://media.codonaft.com
166173
- https://media-cached.codonaft.com

_do.cr

Lines changed: 91 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -60,41 +60,69 @@ CACHE_DIR = BUILD_DIR.join(".cache")
6060
BANLISTS = CACHE_DIR.join("banlists.txt")
6161
BROKEN_POST_URLS = CACHE_DIR.join("broken_post_urls.txt")
6262
COMMON_ROOT = Path["_ohmyvps/alpine/alpine-root"]
63-
MEDIA_BUILD_DIR = BUILD_DIR.join(MEDIA_HOST, "/var/www", PUBLIC_MEDIA_HOST)
63+
64+
SERVER_MEDIA_DIR = Path["var/www"].join(PUBLIC_MEDIA_HOST)
65+
MEDIA_BUILD_DIR = BUILD_DIR.join(MEDIA_HOST, SERVER_MEDIA_DIR)
66+
67+
MIRROR_FROM_TO = [
68+
{MEDIA_HOST, [{MIRROR_HOST, SERVER_MEDIA_DIR}]},
69+
]
6470

6571
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
6672

6773
def main
6874
repo_dir = Path[File.dirname(File.realpath(__FILE__))]
6975
Dir.cd(repo_dir)
7076

77+
ps = processes()
78+
check(ps)
79+
7180
config = YAML.parse(File.read(MAIN_SITE_CONFIG))
7281
if !ARGV.empty? && (ARGV.includes?("-h") || ARGV.includes?("--help"))
7382
usage
7483
elsif ARGV.size == 1 && ARGV[0] == "sync"
75-
check
84+
check_ssh_hosts(ps)
7685
sync
7786
elsif ARGV.size >= 1 && ARGV[0] == "sync-nostr"
7887
profiles = ARGV.size > 1 && ARGV[1] == "profiles"
7988
sync_nostr(config, profiles: profiles, output_relays: ["ws://localhost:7777", "wss://nostr.codonaft.com", "wss://purplepag.es", "wss://nostr.oxtr.dev", "wss://nostr.girino.org"])
8089
elsif ARGV.size == 1 && ARGV[0] == "health"
81-
check
90+
check_ssh_hosts(ps)
8291
health(config)
8392
elsif ARGV.size >= 1 && ARGV[0] == "build"
84-
check
8593
update
8694
build
8795
if ARGV.size == 2 && ARGV[1] == "run"
8896
serve(DEBUG_HOST)
8997
end
9098
elsif ARGV.size > 2 && ARGV[0] == "encode"
91-
check
9299
encode_media(ARGV[1], config, ARGV[2])
93100
elsif ARGV.size == 1 && ARGV[0] == "deploy"
94-
check
101+
check_ssh_hosts(ps)
95102
deploy(config)
103+
elsif ARGV.size == 1 && ARGV[0] == "cloudflare-banlist"
104+
expr_begin = "ip.src in {"
105+
expr_end = "}"
106+
max_len = 4096 - expr_begin.size - expr_end.size
107+
ips = Dir
108+
.glob(".build/*/var/tmp/local-banlist.txt")
109+
.flat_map { |i| File.read_lines(i) }
110+
.map { |i| i.strip }
111+
.select { |i| i.size > 0 }
112+
.sort
113+
.uniq
114+
.join(' ')
115+
while ips.size > 0
116+
end_index = ips.rindex(' ', max_len)
117+
if end_index.nil? || ips.size <= max_len
118+
puts("#{expr_begin}#{ips.strip}#{expr_end}\n\n")
119+
break
120+
else
121+
puts("#{expr_begin}#{ips[0..end_index].strip}#{expr_end}\n\n")
122+
ips = ips[end_index..].strip
123+
end
124+
end
96125
elsif ARGV.empty? || ARGV[0] == "debug"
97-
check
98126
update
99127
build
100128
serve(DEBUG_HOST)
@@ -136,6 +164,9 @@ def usage
136164
#{script} health
137165
run health checks
138166
167+
#{script} cloudflare-banlist
168+
generate ban-lists for Security - WAF
169+
139170
#{script} encode path/to/video-name.mkv <en|ru>
140171
#{script} encode https://youtu.be/CB9bS46vl04 <en|ru>
141172
encode (or download and transcode) media to HLS, put it to #{MEDIA_BUILD_DIR}/video-name/
@@ -158,6 +189,13 @@ end
158189

159190
def build
160191
step("build")
192+
193+
if DEBUG
194+
warn("DEBUG\n")
195+
else
196+
ok("PROD\n")
197+
end
198+
161199
Dir.mkdir_p(CACHE_DIR, 0o755)
162200

163201
build_vidstack_player
@@ -317,6 +355,10 @@ def sync
317355
hosts = all_hosts()
318356
puts("hosts: #{hosts}")
319357

358+
mirror = MIRROR_FROM_TO.map { |source_host, destination_and_files|
359+
{source_host, destination_and_files.select { |destination_host, _| hosts.includes?(destination_host) }}
360+
}.to_h
361+
320362
common_files = git_ls(COMMON_ROOT)
321363
hosts_files : Hash(String, Set(Path)) = hosts.map { |host|
322364
host_dir = HOSTS_DIR.join(host)
@@ -327,7 +369,7 @@ def sync
327369
hosts.map { |host|
328370
done = Channel(Nil).new
329371
spawn do
330-
sync_host(host, hosts_files, common_files)
372+
sync_host(host, hosts_files: hosts_files, common_files: common_files, mirror_to: mirror[host]?)
331373
done.send(nil)
332374
end
333375
done
@@ -336,7 +378,7 @@ def sync
336378
ok("sync finished\n")
337379
end
338380

339-
def sync_host(host : String, hosts_files : Hash(String, Set(Path)), common_files : Set(Path))
381+
def sync_host(host : String, *, hosts_files : Hash(String, Set(Path)), common_files : Set(Path), mirror_to : Array({String, Path}) | Nil)
340382
host_files : Set(Path) = hosts_files[host]
341383
all_hosts_files : Set(Path) = hosts_files
342384
.flat_map { |_, files| files.to_a }
@@ -352,6 +394,15 @@ def sync_host(host : String, hosts_files : Hash(String, Set(Path)), common_files
352394
host_build_files = children_recursive(host_build_dir).to_set
353395
filtered_sync(host, host_build_dir, upload: host_build_files) # TODO: with --delete specifically for jekyll ?
354396

397+
unless mirror_to.nil?
398+
mirror_to.each { |destination_host, dir|
399+
mirror_host_files = host_files.select { |i| i.to_s.starts_with?(dir.to_s) }
400+
mirror_host_build_files = host_build_files.select { |i| i.to_s.starts_with?(dir.to_s) }
401+
filtered_sync(destination_host, HOSTS_DIR.join(host), upload: mirror_host_files.to_set)
402+
filtered_sync(destination_host, BUILD_DIR.join(host), upload: mirror_host_build_files.to_set)
403+
}
404+
end
405+
355406
ssh(host, ["sudo etckeeper commit sync 2>>/dev/null"])
356407
end
357408

@@ -724,41 +775,23 @@ def update_broken_post_urls
724775
broken_urls
725776
end
726777

727-
def check
728-
step("check")
729-
730-
ps = processes()
778+
def check(ps : Array(Tuple(Int64, String)))
731779
current_ps_name = "/#{Path[__FILE__].basename}"
732780
raise "other instance keeps running" if ps
733781
.select { |(_, i)| i.includes?("crystal") && i.includes?(current_ps_name) }
734782
.any? { |(pid, _)| pid != Process.ppid }
735783

736-
puts("checking dependencies")
737-
system("which bsdtar bundle css-minify node pnpm podman rsync scour svgcleaner svgo uglifyjs wget >>/dev/null")
738-
raise "missing deps, run: cd && npm install 'css-minify@2.0.0' 'svgo@3.3.2' && USE='lz4 xxhash zstd' emerge '=app-arch/libarchive-3.7.9' '=app-arch/unzip-6.0_p27-r1' '=app-containers/podman-5.3.2' '=dev-ruby/bundler-2.4.22' '=dev-lang/crystal-1.16.1' '=dev-util/uglifyjs-3.16.1' '=media-gfx/scour-0.38.2-r1' '=media-sound/opus-tools-0.2-r1' '=media-libs/libwebp-1.4.0' '=media-video/mediainfo-24.11' '=net-dns/bind-tools-9.18.0-r1' '=net-libs/nodejs-22.13.1' '=net-misc/rsync-3.4.1' '=net-misc/wget-1.25.0' '=sys-apps/pnpm-bin-9.6.0' && cargo install --locked 'svgcleaner@0.9.5' 'websocat@1.13.0'" unless $?.success?
784+
system("which bsdtar bundle css-minify git nak node pnpm podman rsync scour svgcleaner svgo uglifyjs wget >>/dev/null")
785+
raise "missing deps, run: cd && npm install 'css-minify@2.0.0' 'svgo@3.3.2' && USE='lz4 xxhash zstd' emerge '=app-arch/libarchive-3.7.9' '=app-arch/unzip-6.0_p27-r1' '=app-containers/podman-5.3.2' '=dev-ruby/bundler-2.4.22' '=dev-lang/crystal-1.16.1' '=dev-util/uglifyjs-3.16.1' '=dev-vcs/git-2.49.0-r2' '=media-gfx/scour-0.38.2-r1' '=media-sound/opus-tools-0.2-r1' '=media-libs/libwebp-1.4.0' '=media-video/mediainfo-24.11' '=net-dns/bind-tools-9.18.0-r1' '=net-libs/nodejs-22.13.1' '=net-misc/rsync-3.4.1' '=net-misc/wget-1.25.0' '=sys-apps/pnpm-bin-9.6.0' && cargo install --locked 'svgcleaner@0.9.5' 'websocat@1.13.0' && go install github.com/fiatjaf/nak@latest" unless $?.success?
739786
system("podman ps >>/dev/null")
740787
raise "podman failed" unless $?.success?
741788

742-
system(<<-STRING
743-
set -euo pipefail
744-
#{TRACE ? "set -x" : ""}
745-
for i in $(grep --recursive vim:nofixendofline _includes | cut -d ':' -f1) ; do
746-
if [[ $(wc --lines "$i" | awk '{print $1}') -ne 0 ]] ; then
747-
echo "unexpected newline in $i"
748-
exit 1
749-
fi
750-
done
751-
STRING
752-
)
753-
raise "file format failure" unless $?.success?
754-
755-
check_ssh_hosts(ps)
756-
757-
if DEBUG
758-
warn("DEBUG\n")
759-
else
760-
ok("PROD\n")
761-
end
789+
files_with_unexpected_newlines = `grep --recursive vim:nofixendofline _includes`
790+
.strip
791+
.split('\n')
792+
.map { |i| i.split(':')[0] }
793+
.select { |i| File.read(i).includes?('\n') }
794+
raise "unexpected newline in #{files_with_unexpected_newlines}" unless files_with_unexpected_newlines.empty?
762795
end
763796

764797
def build_rust_app(
@@ -1066,26 +1099,29 @@ end
10661099

10671100
def check_i2p_host(host : String)
10681101
puts("checking i2p configuration at #{host}")
1069-
private_key = File.read_lines(HOSTS_DIR.join(host).join("etc/i2pd/tunnels.conf"))
1070-
.select { |i| i.starts_with?("keys =") }
1071-
.map { |i| i.split(" = ")[1] }[0]
10721102
service_dir = Path["/var/lib/i2pd"]
1103+
private_keys = File.read_lines(HOSTS_DIR.join(host).join("etc/i2pd/tunnels.conf"))
1104+
.select { |i| i.starts_with?("keys =") }
1105+
.map { |i| i.split(" = ")[1] }
10731106
check_manual_upload(host, owner: "i2pd", group: "i2pd", mode: 700, path: service_dir)
1074-
check_manual_upload(host, owner: "i2pd", group: "i2pd", mode: 440, path: service_dir.join(private_key))
1107+
private_keys.each { |i| check_manual_upload(host, owner: "i2pd", group: "i2pd", mode: 440, path: service_dir.join(i)) }
10751108
end
10761109

10771110
def check_tor_host(host : String)
10781111
puts("checking tor configuration at #{host}")
1079-
service_dir = Path[File.read_lines(HOSTS_DIR.join(host).join("etc/tor/torrc"))
1112+
service_dirs = File.read_lines(HOSTS_DIR.join(host).join("etc/tor/torrc"))
10801113
.select { |i| i.starts_with?("HiddenServiceDir") }
1081-
.map { |i| i.split(" ")[1] }[0]]
1082-
check_manual_upload(host, owner: "tor", group: "tor", mode: 700, path: service_dir)
1083-
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: service_dir.join("hs_ed25519_secret_key"))
1084-
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: service_dir.join("hs_ed25519_public_key"))
1085-
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: service_dir.join("hostname"), data: service_dir.basename)
1114+
.map { |i| Path[i.split(" ")[1]] }
1115+
service_dirs.map { |i|
1116+
check_manual_upload(host, owner: "tor", group: "tor", mode: 700, path: i)
1117+
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: i.join("hs_ed25519_secret_key"))
1118+
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: i.join("hs_ed25519_public_key"))
1119+
check_manual_upload(host, owner: "tor", group: "tor", mode: 400, path: i.join("hostname"), data: i.basename)
1120+
}
10861121
end
10871122

10881123
def check_ssh_hosts(ps : Array(Tuple(Int64, String)))
1124+
step("check_ssh_hosts")
10891125
all_hosts()
10901126
.map { |i|
10911127
check_existing_ssh_connection(i, ps)
@@ -1132,12 +1168,15 @@ def check_existing_ssh_connection(host : String, ps : Array(Tuple(Int64, String)
11321168
end
11331169

11341170
def check_manual_upload(host : String, *, owner : String, group : String, mode : UInt16, path : Path, data : String? = nil)
1135-
raise "#{path}: unexpected owner" unless ssh(host, ["sudo stat -c %U #{path}"]) == owner
1136-
raise "#{path}: unexpected group" unless ssh(host, ["sudo stat -c %G #{path}"]) == group
1137-
raise "#{path}: unexpected mode" unless ssh(host, ["sudo stat -c %a #{path}"]).to_i == mode
1138-
unless data.nil?
1139-
raise "#{path}: unexpected data" unless ssh(host, ["sudo cat #{path}"]).strip == data
1140-
end
1171+
commands = ["stat -c %U,%G,%a"]
1172+
commands += ["cat"] unless data.nil?
1173+
output = ssh(host, commands.map { |i| "sudo #{i} #{path}" }).strip.split('\n')
1174+
stat = output[0].split(',')
1175+
1176+
raise "#{path}: unexpected owner" unless stat[0] == owner
1177+
raise "#{path}: unexpected group" unless stat[1] == group
1178+
raise "#{path}: unexpected mode" unless stat[2].to_i == mode
1179+
raise "#{path}: unexpected data" unless data.nil? || output[1] == data
11411180
end
11421181

11431182
def sync_nostr(config, *, profiles : Bool, output_relays : Array(String))

_hosts/media.codonaft/etc/i2pd/tunnels.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[anon-website]
1+
[codonaft-media]
22
type = http
33
host = 127.0.0.1
44
port = 80

0 commit comments

Comments
 (0)