Skip to content

Commit fb25905

Browse files
committed
Update
1 parent 260b279 commit fb25905

16 files changed

Lines changed: 455 additions & 169 deletions

File tree

_config.i2p.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,13 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p
7+
nostr:
8+
spam_api_overwrite: http://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/spam.nostr.band/spam_api
9+
profile_relays_overwrite:
10+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/purplepag.es
11+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/hist.nostr.land
12+
relays_overwrite:
13+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr
14+
read_cache_relays_overwrite:
15+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr
16+
- ws://codnaft43k7ncna2hfsxrzi2nqoxieu22vbyjkmhkwdrrta2ghlq.b32.i2p/nostr/relay.primal.net

_config.tor.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,14 @@ theme_settings:
44
p2p_player:
55
media_urls_overwrite:
66
- http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion
7+
nostr:
8+
spam_api_overwrite: http://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr/spam.nostr.band/spam_api
9+
profile_relays_overwrite:
10+
- wss://purplepag.es
11+
- wss://hist.nostr.land
12+
relays_overwrite:
13+
- ws://oxtrdevav64z64yb7x6rjg4ntzqjhedm5b5zjqulugknhzr46ny2qbad.onion
14+
- ws://gnostr2jnapk72mnagq3cuykfon73temzp77hcbncn4silgt77boruid.onion
15+
read_cache_relays_overwrite:
16+
- ws://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr
17+
- ws://codonaftct3jsouvfyrjq4yumyngzv3el2msndf5oddccktgghnw7eyd.onion/nostr/relay.primal.net

_config.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,27 @@ theme_settings:
126126
zapthreads: true
127127

128128
nostr:
129+
enabled: true
129130
npub: npub1alptdev5srcw2hxg03567p4k6xs3lgj7f6545suc0rzp0xw98svse7rg94
130131
community: codonaft
131132
max_comment_length: 5000
132133
min_read_pow: 7
134+
135+
spam_api: https://spam.nostr.band/spam_api
136+
profile_relays:
137+
- wss://purplepag.es
138+
- wss://hist.nostr.land
133139
relays:
134140
- wss://nostr-pub.wellorder.net
135141
- wss://relay.nostr.band
136142
read_cache_relays:
137143
- wss://relay.primal.net
138144

145+
spam_api_overwrite: ''
146+
profile_relays_overwrite: []
147+
relays_overwrite: []
148+
read_cache_relays_overwrite: []
149+
139150
special_page:
140151
search:
141152
icon: search # Assuming page link and icon are the same

_do.cr

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require "json"
1212
require "uri"
1313
require "yaml"
1414

15-
DEBUG = ARGV.empty? || !["sync", "build", "deploy", "health"].includes?(ARGV[0])
15+
DEBUG = ARGV.empty? || !["sync", "sync-nostr", "build", "deploy", "health"].includes?(ARGV[0])
1616
TRACE = ENV["TRACE"]? == "1"
1717

1818
WILDCARD_HOST = "0.0.0.0"
@@ -74,6 +74,9 @@ def main
7474
elsif ARGV.size == 1 && ARGV[0] == "sync"
7575
check
7676
sync
77+
elsif ARGV.size >= 1 && ARGV[0] == "sync-nostr"
78+
profiles = ARGV.size > 1 && ARGV[1] == "profiles"
79+
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"])
7780
elsif ARGV.size == 1 && ARGV[0] == "health"
7881
check
7982
health(config)
@@ -126,6 +129,10 @@ def usage
126129
download newest non-conflicting versioned files to _ohmyvps and _hosts
127130
upload newest files from _ohmyvps, _hosts and .build
128131
132+
#{script} sync-nostr [profile]
133+
fetch nostr events, push them to my relays
134+
don't send events to profile-specific relays by default
135+
129136
#{script} health
130137
run health checks
131138
@@ -392,6 +399,10 @@ def health(config)
392399
end
393400
step("health")
394401

402+
all_hosts().each { |host| ssh(host, ["sudo /etc/init.d/nginx checkconfig"], tty: true) }
403+
404+
warn("github commit hash is different\n") unless JSON.parse(HTTP::Client.get("https://api.github.com/repos/codonaft/codonaft.github.io/commits").body)[0]["sha"].as_s == `git rev-parse HEAD`.strip
405+
395406
banlists =
396407
if nonempty_exists?(BANLISTS) && File.info(BANLISTS).modification_time + 1.days > Time.utc
397408
File.read_lines(BANLISTS).to_set
@@ -461,7 +472,7 @@ def health(config)
461472
.map { |i| i.strip }
462473
.reject { |i| i.empty? }
463474
.map { |i| URI.parse(i) }
464-
.reject { |i| i.hostname.nil? || i.hostname == "127.0.0.1" } + relay_mirrors
475+
.reject { |i| i.hostname.nil? || i.hostname == "localhost" } + relay_mirrors
465476
wss_uris = (trackers + other_relays + `git grep --only-matching 'wss://[a-zA-Z0-9/._-]*'`
466477
.split('\n')
467478
.map { |i| i.strip.gsub(/.*wss:\/\//, "") }
@@ -714,7 +725,6 @@ def check
714725
step("check")
715726

716727
ps = processes()
717-
718728
current_ps_name = "/#{Path[__FILE__].basename}"
719729
raise "other instance keeps running" if ps
720730
.select { |(_, i)| i.includes?("crystal") && i.includes?(current_ps_name) }
@@ -723,6 +733,8 @@ def check
723733
puts("checking dependencies")
724734
system("which bsdtar bundle css-minify node pnpm podman rsync scour svgcleaner svgo uglifyjs wget >>/dev/null")
725735
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?
736+
system("podman ps >>/dev/null")
737+
raise "podman failed" unless $?.success?
726738

727739
system(<<-STRING
728740
set -euo pipefail
@@ -1120,6 +1132,117 @@ def check_manual_upload(host : String, *, owner : String, group : String, mode :
11201132
end
11211133
end
11221134

1135+
def sync_nostr(config, *, profiles : Bool, output_relays : Array(String))
1136+
# TODO: do the same thing as cron bash job that uses rust nostr cli client? connect to relays over tor?
1137+
1138+
limit = 100
1139+
now = Time.utc
1140+
from = now - 3.years # TODO: last commit date? last blog date? previous blog date?
1141+
to = now + 15.minutes
1142+
profile_kinds = [0, 3, 10002]
1143+
1144+
nostr_config = config["theme_settings"]["nostr"]
1145+
npub = nostr_config["npub"].as_s
1146+
pk = JSON.parse(`nak decode #{npub}`)["pubkey"].as_s
1147+
1148+
profile_relays = nostr_config["profile_relays"].as_a.map { |i| i.as_s }
1149+
relays = nostr_config["relays"].as_a.map { |i| i.as_s }
1150+
read_cache_relays = nostr_config["read_cache_relays"].as_a.map { |i| i.as_s }
1151+
input_relays : Array(String) = relays + read_cache_relays + profile_relays
1152+
1153+
step("sync-nostr")
1154+
trace("pk=#{pk} input_relays=#{input_relays} output_relays=#{output_relays}")
1155+
1156+
nak = "nak req --limit #{limit} --since #{from.to_unix} --until #{to.to_unix}"
1157+
1158+
mentions = parse_nostr_events(`#{nak} -p #{pk} #{input_relays.join(' ')}`)
1159+
.reject { |i|
1160+
return false unless i["tags"].as_a.any? { |t| t.size > 1 && t[0] == "p" && t[1] == pk }
1161+
k = i["kind"].as_i
1162+
[3, 1984, 4454].includes?(k) || (k >= 5000 && k <= 7000) || (k >= 10000 && k <= 10102) || (k >= 20000 && k < 30000) || (k >= 30000 && k <= 30267)
1163+
}
1164+
puts("fetched #{mentions.size} mentions")
1165+
1166+
parsed_authored_events = parse_nostr_events(`#{nak} -a #{pk} #{input_relays.join(' ')}`)
1167+
authored_events = parsed_authored_events
1168+
.reject { |i|
1169+
return false unless i["pubkey"].as_s == pk
1170+
k = i["kind"].as_i
1171+
[4454, 10044].includes?(k) || (k >= 20000 && k < 30000)
1172+
}
1173+
puts("fetched #{authored_events.size} authored events")
1174+
1175+
puts("writing events")
1176+
nak_raw(["event"] + output_relays, (mentions + authored_events).map { |i| i.to_json })
1177+
1178+
if profiles
1179+
# TODO: check spam
1180+
fetch_comments_command = ([nak, "-k", "1", "-p", pk] + input_relays).join(' ')
1181+
trace(fetch_comments_command)
1182+
comments_pks = parse_nostr_events(`#{fetch_comments_command}`)
1183+
.select { |i| i["kind"].as_i == 1 && i["pubkey"].as_s != pk }
1184+
.map { |i| i["pubkey"].as_s }.to_set
1185+
request_profiles_command = (
1186+
[nak, "--since", "0", "-k", "0"] + comments_pks.map { |i| "-a #{i}" } + input_relays
1187+
).join(' ')
1188+
trace(request_profiles_command)
1189+
comments_profile_events = parse_nostr_events(`#{request_profiles_command}`)
1190+
.select { |i| i["kind"].as_i == 0 }
1191+
.group_by { |i| i["pubkey"].as_s }
1192+
.reject { |p, _| p == pk }
1193+
.map { |_, g| g.max_by { |i| i["created_at"].as_i } }
1194+
puts("profile events of commenters = #{comments_profile_events.size}")
1195+
1196+
request_author_profile_events_command = (
1197+
[nak, "--since", "0", "-a", pk] + profile_kinds.map { |k| "-k #{k}" } + input_relays
1198+
).join(' ')
1199+
trace(request_author_profile_events_command)
1200+
author_profile_events = parse_nostr_events(`#{request_author_profile_events_command}`)
1201+
.select { |i| profile_kinds.includes?(i["kind"].as_i) }
1202+
puts("author profile events = #{author_profile_events.size}")
1203+
nak_raw(["event"] + profile_relays, (comments_profile_events + author_profile_events).map { |i| i.to_json })
1204+
end
1205+
puts("finished writing events")
1206+
end
1207+
1208+
def nak_raw(args, input : Array(String) = [] of String)
1209+
# TODO: exit after timeout
1210+
output = Channel(Tuple(String, Process::Status)).new
1211+
trace("running nak #{args}")
1212+
spawn do
1213+
value = Process.run(command: "nak", args: args) do |p|
1214+
input.each do |line|
1215+
trace("writing stdin")
1216+
p.input.puts(line)
1217+
trace("writing stdin ok")
1218+
end
1219+
trace("closing stdin")
1220+
p.input.close
1221+
trace("closed stdin")
1222+
trace("reading stdout")
1223+
result = p.output.gets_to_end
1224+
trace("finished reading stdout")
1225+
result
1226+
end
1227+
trace("sending stdout")
1228+
output.send({value.strip, $?})
1229+
trace("sent stdout")
1230+
end
1231+
trace("waiting for stdout")
1232+
value, status = output.receive
1233+
trace("received stdout")
1234+
raise "#{args}: unexpected exit code #{status}, value=#{value}" unless status.success?
1235+
value
1236+
end
1237+
1238+
def parse_nostr_events(text)
1239+
text
1240+
.split('\n')
1241+
.reject { |i| i.strip.empty? }
1242+
.to_set
1243+
.map { |i| JSON.parse(i) }
1244+
end
1245+
11231246
def processes
11241247
Dir.entries("/proc")
11251248
.select { |entry| entry =~ /^\d+$/ }

_hosts/media.codonaft/etc/init.d/rnostr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ name="rnostr"
55
command="/usr/local/bin/${name}"
66
command_user="${name}:${name}"
77
command_background="true"
8-
command_args="relay --watch -c /etc/${name}/${name}.toml"
8+
command_args="relay -c /etc/${name}/${name}.toml"
9+
retry="60"
910

1011
pidfile="/run/${name}.pid"
1112
output_logger="logger"
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
#!/usr/bin/env sh
1+
#!/usr/bin/env bash
22

33
[ -f /etc/init.d/aquatic_ws ] && [ -d /var/www ] && \
4-
inotifywait --event close_write,moved_to,create --monitor --recursive /etc/aquatic/ /etc/init.d/ /var/www/ |
4+
inotifywait --event close_write,moved_to,create --monitor --recursive /etc/aquatic/ /var/www/ |
55
while read -r directory events filename ; do
6-
if [[ "${filename}" == "aquatic_ws.conf" || "${filename}" == "aquatic_ws" || "${filename}" == *".m3u8" ]] ; then
6+
if [[ "${filename}" == "aquatic_ws.conf" || "${filename}" == *".m3u8" ]] ; then
77
rc-service --ifstarted aquatic_ws reload 2>>/dev/stdout | logger
88
fi
99
done &
1010

11-
[ -f /etc/init.d/broadcastr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/broadcastr/ /etc/conf.d/ |
11+
[ -f /etc/init.d/broadcastr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/broadcastr/ |
1212
while read -r directory events filename ; do
13-
if [[ "${filename}" == *".json" || "${filename}" == "broadcastr" ]] ; then
13+
if [[ "${filename}" == *".json" ]] ; then
1414
rc-service --ifstarted broadcastr restart 2>>/dev/stdout | logger
1515
fi
1616
done &
1717

18-
# using rnostr relay --watch for now, however it's unclear whether it's working correctly
19-
# [ -f /etc/init.d/rnostr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/rnostr/ |
20-
# while read -r directory events filename ; do
21-
# if [[ "${filename}" == "rnostr.toml" ]] ; then
22-
# rc-service --ifstarted rnostr restart 2>>/dev/stdout | logger
23-
# fi
24-
# done &
18+
# rnostr relay --watch does nothing (probably under load only)
19+
[ -f /etc/init.d/rnostr ] && inotifywait --event close_write,moved_to,create --monitor --recursive /etc/rnostr/ |
20+
while read -r directory events filename ; do
21+
if [[ "${filename}" == "rnostr.toml" ]] ; then
22+
rc-service --ifstarted rnostr restart 2>>/dev/stdout | logger
23+
fi
24+
done &

_hosts/media.codonaft/etc/nginx/http.d/media.conf

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ server {
6464
}
6565

6666
location ~ \.(m3u8|mp4|ts|vtt|webp)$ {
67+
if ($http_origin != $origin) {
68+
return 404;
69+
}
70+
6771
add_header "Access-Control-Allow-Origin" $origin always;
6872

6973
add_header Cache-Control "public, max-age=31536000, immutable";
@@ -132,6 +136,10 @@ server {
132136
ignore_invalid_headers on;
133137

134138
location ~ \.(m3u8|mp4|ts|vtt|webp)$ {
139+
if ($http_origin != $origin) {
140+
return 404;
141+
}
142+
135143
add_header "Access-Control-Allow-Origin" $origin always;
136144

137145
add_header Cache-Control "public, max-age=31536000, immutable";
@@ -167,4 +175,80 @@ server {
167175

168176
proxy_pass http://localhost:6666/;
169177
}
178+
179+
location /nostr/relay.primal.net {
180+
if ($http_origin != $origin) {
181+
return 404;
182+
}
183+
184+
resolver 9.9.9.9 1.1.1.1 ipv6=off valid=300s;
185+
186+
proxy_hide_header "Host";
187+
proxy_set_header "Host" "relay.primal.net";
188+
proxy_hide_header "Access-Control-Allow-Origin";
189+
190+
proxy_http_version 1.1;
191+
proxy_connect_timeout 10m;
192+
proxy_socket_keepalive on;
193+
proxy_send_timeout 365d;
194+
proxy_read_timeout 365d;
195+
196+
proxy_set_header Upgrade $http_upgrade;
197+
proxy_set_header Connection "upgrade";
198+
199+
proxy_ssl_server_name on;
200+
set $target https://relay.primal.net/;
201+
proxy_pass $target;
202+
}
203+
204+
location ~ /nostr/(hist\.nostr\.land|purplepag\.es)$ {
205+
if ($http_origin != "http://codonaft.i2p") {
206+
return 404;
207+
}
208+
209+
resolver 9.9.9.9 1.1.1.1 ipv6=off valid=300s;
210+
211+
proxy_hide_header "Host";
212+
proxy_set_header "Host" $1;
213+
proxy_hide_header "Access-Control-Allow-Origin";
214+
215+
proxy_http_version 1.1;
216+
proxy_connect_timeout 10m;
217+
proxy_socket_keepalive on;
218+
proxy_send_timeout 365d;
219+
proxy_read_timeout 365d;
220+
221+
proxy_set_header Upgrade $http_upgrade;
222+
proxy_set_header Connection "upgrade";
223+
224+
proxy_ssl_server_name on;
225+
set $target https://$1/;
226+
proxy_pass $target;
227+
}
228+
229+
location ~ /nostr/(spam\.nostr\.band/spam_api.*)$ {
230+
if ($http_origin != $origin) {
231+
return 404;
232+
}
233+
234+
resolver 9.9.9.9 1.1.1.1 ipv6=off valid=300s;
235+
236+
proxy_hide_header "Host";
237+
proxy_set_header "Host" "spam.nostr.band";
238+
proxy_hide_header "Access-Control-Allow-Origin";
239+
add_header "Access-Control-Allow-Origin" $origin always;
240+
241+
add_header Cache-Control "public, max-age=86400";
242+
expires 86400s;
243+
244+
proxy_http_version 1.1;
245+
proxy_connect_timeout 10m;
246+
proxy_socket_keepalive on;
247+
proxy_send_timeout 10m;
248+
proxy_read_timeout 10m;
249+
250+
proxy_ssl_server_name on;
251+
set $target https://$1$is_args$args;
252+
proxy_pass $target;
253+
}
170254
}

0 commit comments

Comments
 (0)