--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+my $GIT_ROOT = '/var/lib/git';
+
+my $repo = $ENV{GIT_REPO};
+my $blob_id = $ENV{GIT_ID} || $ENV{GIT_BLOB_ID};
+
+$repo =~ s{[^a-zA-Z0-9._-]}{}g;
+$blob_id =~ s{[^0-9a-f]}{}g;
+
+die unless $repo;
+die unless $blob_id;
+
+open(my $fh, '-|', 'git', "--git-dir=$GIT_ROOT/$repo",
+ 'cat-file', 'blob', $blob_id)
+ or die "git cat-file failed: $!";
+
+# Читаем первые 8000 байт
+my $buf = '';
+read($fh, $buf, 8000);
+
+# Определяем тип по наличию нулевых байт. Так делает сам git
+my $is_binary = index($buf, "\x00") >= 0;
+my $ct = $is_binary
+ ? 'application/octet-stream'
+ : 'text/plain; charset=utf-8';
+
+binmode(STDOUT, ':raw');
+print "Content-Type: $ct\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+# Выводим буфер и остаток
+print $buf;
+while (read($fh, my $chunk, 65536)) {
+ print $chunk;
+}
+
+close($fh);
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+my $GIT_ROOT = '/var/lib/git';
+my $PER_PAGE = 20;
+my $BASE_URL = 'https://gitweb.nataraj.world';
+
+my $repo = $ENV{GIT_REPO};
+my $branch = $ENV{GIT_BRANCH} // 'master';
+my $offset = int($ENV{GIT_OFFSET} // 0);
+my $nonce = $ENV{GIT_NONCE} || $ENV{REQUEST_ID};
+
+
+$repo =~ s{[^a-zA-Z0-9._-]}{}g;
+$branch =~ s{[^a-zA-Z0-9._/\-]}{}g;
+$offset = 0 if $offset < 0;
+$nonce =~ s{[^a-zA-Z0-9._-]}{}g;
+
+die unless $repo;
+die unless $branch;
+
+my $rand = int(rand(1_000_000_000));
+my $git_dir = "$GIT_ROOT/$repo";
+
+print "Content-Type: text/html; charset=utf-8\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+open(my $fh, '-|', 'git', "--git-dir=$git_dir", 'log',
+ '--format=%H%x09%s%x09%an%x09%ad',
+ '--date=short',
+ "--skip=$offset",
+ "--max-count=$PER_PAGE",
+ "refs/heads/$branch")
+ or die "git failed: $!";
+
+my @commits;
+while (<$fh>) {
+ chomp;
+ my ($hash, $subject, $author, $date) = split /\t/, $_, 4;
+ push @commits, {
+ hash => $hash,
+ subject => $subject,
+ author => $author,
+ date => $date,
+ };
+}
+close($fh);
+
+my $prev = $offset - $PER_PAGE; $prev = 0 if $prev < 0;
+my $next = $offset + $PER_PAGE;
+
+print <<HTML;
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>git log: $repo / $branch</title>
+</head>
+<body>
+<h2>git log: $repo / $branch (offset: $offset)</h2>
+Nonce: $nonce <br><br>
+HTML
+
+print "<a href=\"$BASE_URL/gitai-log/$repo/$branch/$prev/r$rand\">← новее</a> "
+ if $offset > 0;
+print "<a href=\"$BASE_URL/gitai-log/$repo/$branch/$next/r$rand\">старее →</a>"
+ if @commits == $PER_PAGE;
+
+print "\n<table border='1' cellpadding='4'>\n";
+print "<tr><th>Diff</th><th>Дерево</th><th>Дата</th><th>Автор</th><th>Сообщение</th></tr>\n";
+
+for my $c (@commits) {
+ my $short = substr($c->{hash}, 0, 8);
+ my $show_url = "$BASE_URL/gitai-show/$repo/$c->{hash}";
+ my $tree_url = "$BASE_URL/gitai-ls-tree/$repo/$c->{hash}/0/r$rand";
+ print "<tr>";
+ print "<td><a href=\"$show_url\">$short</a></td>";
+ print "<td><a href=\"$tree_url\">tree</a></td>";
+ print "<td>$c->{date}</td>";
+ print "<td>$c->{author}</td>";
+ print "<td>$c->{subject}</td>";
+ print "</tr>\n";
+}
+
+print "</table>\n";
+print "</body></html>\n";
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+my $GIT_ROOT = '/var/lib/git';
+my $PER_PAGE = 50;
+my $BASE_URL = 'https://gitweb.nataraj.world';
+
+my $repo = $ENV{GIT_REPO};
+my $commit_id = $ENV{GIT_ID} || $ENV{GIT_COMMIT_ID};
+my $offset = int($ENV{GIT_OFFSET} // 0);
+
+$repo =~ s{[^a-zA-Z0-9._-]}{}g;
+$commit_id =~ s{[^0-9a-f]}{}g;
+$offset = 0 if $offset < 0;
+
+die unless $repo;
+die unless $commit_id;
+
+my $rand = int(rand(1_000_000_000));
+my $git_dir = "$GIT_ROOT/$repo";
+
+print "Content-Type: text/html; charset=utf-8\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+# Показываем информацию о коммите
+open(my $log, '-|', 'git', "--git-dir=$git_dir", 'log',
+ '-1', '--format=%H %s (%an, %ad)', '--date=short', $commit_id)
+ or die "git log failed: $!";
+my $commit_info = <$log> // '';
+chomp $commit_info;
+close($log);
+
+# Получаем все файлы
+open(my $fh, '-|', 'git', "--git-dir=$git_dir", 'ls-tree',
+ '-r', '--long', $commit_id)
+ or die "git ls-tree failed: $!";
+
+my @files;
+while (<$fh>) {
+ chomp;
+ if (/^(\S+)\s+blob\s+([0-9a-f]{40})\s+(\S+)\t(.+)$/) {
+ push @files, { hash => $2, size => $3, name => $4 };
+ }
+}
+close($fh);
+
+my $total = scalar @files;
+my @page = grep { defined } @files[$offset .. $offset + $PER_PAGE - 1];
+
+my $prev = $offset - $PER_PAGE; $prev = 0 if $prev < 0;
+my $next = $offset + $PER_PAGE;
+
+print <<HTML;
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8">
+<title>git ls-tree: $repo / $commit_id</title>
+</head>
+<body>
+<h2>git ls-tree: $repo / $commit_id</h2>
+<p>$commit_info</p>
+<p>Файлов: $total, показаны $offset — @{[$offset + scalar @page]}</p>
+HTML
+
+print "<a href=\"$BASE_URL/gitai-ls-tree/$repo/$commit_id/$prev/r$rand\">← выше</a> "
+ if $offset > 0;
+print "<a href=\"$BASE_URL/gitai-ls-tree/$repo/$commit_id/$next/r$rand\">ниже →</a>"
+ if $next < $total;
+
+print "\n<table border='1' cellpadding='4'>\n";
+print "<tr><th>Блоб</th><th>Размер</th><th>Файл</th></tr>\n";
+
+for my $f (@page) {
+ my $raw_url = "$BASE_URL/gitai-cat-file/$repo/$f->{hash}/r$rand";
+ my $short = substr($f->{hash}, 0, 8);
+ printf "<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%s</td></tr>\n",
+ $raw_url, $short, $f->{size}, $f->{name};
+}
+
+print "</table>\n";
+print "</body></html>\n";
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+my $GIT_ROOT = '/var/lib/git';
+
+my $repo = $ENV{GIT_REPO};
+my $commit = $ENV{GIT_ID} || $ENV{GIT_COMMIT};
+
+$repo =~ s{[^a-zA-Z0-9._-]}{}g;
+$commit =~ s{[^0-9a-f]}{}g;
+
+die unless $repo;
+die unless $commit;
+
+print "Content-Type: text/plain; charset=utf-8\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+exec('git', "--git-dir=$GIT_ROOT/$repo", 'show', $commit);
--- /dev/null
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+my $BASE_URL = 'https://gitweb.nataraj.world';
+my $LINKS = 50;
+
+my $repo = $ENV{GIT_REPO};
+my $branch = $ENV{GIT_BRANCH};
+
+$repo =~ s{[^a-zA-Z0-9._-]}{}g;
+$branch =~ s{[^a-zA-Z0-9._/\-]}{}g;
+
+die unless $repo;
+die unless $branch;
+
+print "Content-Type: text/html; charset=utf-8\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+print <<HTML;
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8">
+<title>gitai: $repo / $branch</title>
+</head>
+<body>
+
+<p>Это web entry point для ИИ для git репозитория $repo, ветки $branch.</p>
+
+<p>Коммит лог для ветки доступен по ссылке <a href="$BASE_URL/gitai1-log/$repo/$branch/0">вот этой</a> (страница не кешируется)</p>
+
+<p>Прочитав коммит лог, ты сможешь посмотреть на ls-tree для заданного коммита по ссылке вида https://gitweb.nataraj.world/gitai-ls-tree/$repo/[commit_id]</p>
+
+<p>Так же посмотреть на блобы упомянуте в ls-tree по ссылке вида https://gitweb.nataraj.world/gitai-raw/$repo/[blob_id]</p>
+
+<p>А так же на diff самих коммитов по ссылке вида https://gitweb.nataraj.world/gitai-show/[commit_id]</p>
+
+<p>Полный текст ссылок будет присутвовать на соответсвующих страницах, так что ограничений на fetch у тебя не будет</p>
+
+<p>Используй это знание для того чтобы получать ифнормацию из репозитория $repo/</p>
+
+
+HTML
+
+=cut
+<h2>Список одноразовых ссылок на незакешированный git log для репозитория $repo ветки $branch</h2>
+<ol>
+HTML
+
+for (1..$LINKS) {
+ my $rand = int(rand(1_000_000_000));
+ print "<li><a href=\"$BASE_URL/gitai-log/$repo/$branch/0/r$rand\">r$rand</a></li>\n";
+}
+=cut
+
+print "</ol></body></html>\n";
--- /dev/null
+git4ai-cat-file.pl
\ No newline at end of file
--- /dev/null
+git4ai-log.pl
\ No newline at end of file
--- /dev/null
+git4ai-ls-tree.pl
\ No newline at end of file
--- /dev/null
+git4ai-show.pl
\ No newline at end of file
--- /dev/null
+git4ai.pl
\ No newline at end of file
--- /dev/null
+map $uri $git4ai_script {
+
+ ~^/git4ai-show/ /srv/cgi/git4ai-show.pl;
+ ~^/git4ai-cat-file/ /srv/cgi/git4ai-cat-file.pl;
+ ~^/git4ai-log/ /srv/cgi/git4ai-log.pl;
+ ~^/git4ai-ls-tree/ /srv/cgi/git4ai-ls-tree.pl;
+ ~^/git4ai/ /srv/cgi/git4ai.pl;
+}
+
+server {
+ listen 80;
+
+ location /index.cgi {
+ root /usr/share/gitweb/;
+ include fastcgi_params;
+ gzip off;
+
+ fastcgi_param LANG "en_US.UTF-8";
+ fastcgi_param LC_ALL "en_US.UTF-8";
+ fastcgi_param PERL_UNICODE "SD"; # S=stdin/stdout UTF-8, D=@ARGV UTF-8
+
+ fastcgi_param SCRIPT_NAME $uri;
+ fastcgi_param GITWEB_CONFIG /etc/gitweb.conf;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+
+ # Не кешируем. Иначе колд айай начинает видеть старые версии вместо актуальных
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ add_header Pragma "no-cache" always;
+ expires -1;
+ }
+
+ location / {
+ root /usr/share/gitweb/;
+ index index.cgi;
+
+ # Не кешируем. Иначе колд айай начинает видеть старые версии вместо актуальных
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ add_header Pragma "no-cache" always;
+ expires -1;
+ }
+
+
+ location /gitweb3/index.cgi {
+ root /usr/share/gitweb/;
+ include fastcgi_params;
+ gzip off;
+
+ fastcgi_param LANG "en_US.UTF-8";
+ fastcgi_param LC_ALL "en_US.UTF-8";
+ fastcgi_param PERL_UNICODE "D"; # S=stdin/stdout UTF-8 (убрано), D=@ARGV UTF-8
+
+ fastcgi_param SCRIPT_NAME $uri;
+ fastcgi_param SCRIPT_FILENAME /usr/share/gitweb/index.cgi;
+
+ fastcgi_param GITWEB_CONFIG /etc/gitweb.conf;
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+
+ # Не кешируем. Иначе колд айай начинает видеть старые версии вместо актуальных
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ add_header Pragma "no-cache" always;
+ expires -1;
+ }
+
+ location /gitweb3 {
+ root /usr/share/gitweb/;
+ # index index.cgi;
+
+ try_files $uri /gitweb3/index.cgi$is_args$args;
+
+ # Не кешируем. Иначе колд айай начинает видеть старые версии вместо актуальных
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ add_header Pragma "no-cache" always;
+ expires -1;
+ }
+
+
+
+location ~ "^/raw/([^/]+)/([0-9a-f]{4,40})$" {
+ rewrite "^/raw/([^/]+)/([0-9a-f]{4,40})$" /?p=$1&a=blob_plain&h=$2 break;
+ proxy_pass http://127.0.0.1:80;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+
+location = /raw/ {
+ default_type text/plain;
+ return 200 "https://gitweb.nataraj.world/raw/articles.git/\n";
+ add_header Cache-Control "no-store" always;
+}
+
+
+ # Временная копия на то время пока не протух кеш
+location /gitweb2/ {
+ proxy_pass http://127.0.0.1:80/;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ add_header Pragma "no-cache" always;
+ expires -1;
+}
+
+
+# Если без nonce'а добавляем nonce.
+location ~ "^/git4ai-log/([^/]+)/([^/]+)/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/git4ai-log/$1/$2/$3/$4/$request_id;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/git4ai-log/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/git4ai-log/$1/$2/-/0;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/git4ai-log/([^/]+)/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/git4ai-log/$1/$2/-/$3;
+ add_header Cache-Control "no-store" always;
+}
+
+# repo branch id offset nonce
+location ~ "^/git4ai[^/]*/([a-zA-Z0-9._-]+)/([a-zA-Z0-9._-]+|-)/([0-9a-f]{4,40}|-)/([0-9]+|-)(?:/([a-zA-Z0-9]+))?$" {
+ set $git_repo $1;
+ set $git_branch $2;
+ set $git_id $3;
+ set $git_offset $4;
+ set $git_nonce $5;
+
+ # Должно идти после того как все $N переменные использованы. Так как map $git4ai_scrit их портит
+ fastcgi_param SCRIPT_FILENAME $git4ai_script;
+ fastcgi_param GIT_REPO $git_repo;
+ fastcgi_param GIT_BRANCH $git_branch;
+ fastcgi_param GIT_ID $git_id;
+ fastcgi_param GIT_OFFSET $git_offset;
+ fastcgi_param GIT_NONCE $git_nonce;
+
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+location ~ "^/gitai1/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/gitai/$1/$2/r$request_id;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/gitai/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/gitai/$1/$2/r$request_id;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/gitai/([a-zA-Z0-9._-]+)/([a-zA-Z0-9._-]+)/[a-zA-Z0-9]+$" {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /srv/cgi/gitai.pl;
+ fastcgi_param GIT_REPO $1;
+ fastcgi_param GIT_BRANCH $2;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+location ~ "^/gitai-log/([^/]+)/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/gitai-log/$1/$2/$3/$request_id;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/gitai1-log/([^/]+)/([^/]+)/([^/]+)$" {
+ return 302 https://gitweb.nataraj.world/gitai-log/$1/$2/$3/$request_id;
+ add_header Cache-Control "no-store" always;
+}
+
+location ~ "^/gitai-log/([a-zA-Z0-9._-]+)/([a-zA-Z0-9._-]+)/([0-9]+)/[a-zA-Z0-9]+$" {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /srv/cgi/gitai-log.pl;
+ fastcgi_param GIT_REPO $1;
+ fastcgi_param GIT_BRANCH $2;
+ fastcgi_param GIT_OFFSET $3;
+ fastcgi_param REQUEST_ID $request_id;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+location ~ "^/gitai-ls-tree/([a-zA-Z0-9._-]+)/([0-9a-f]{4,40})/([0-9]+)(?:/[a-zA-Z0-9]+)?$" {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /srv/cgi/gitai-ls-tree.pl;
+ fastcgi_param GIT_REPO $1;
+ fastcgi_param GIT_COMMIT_ID $2;
+ fastcgi_param GIT_OFFSET $3;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+location ~ "^/gitai-show/([^/]+)/([0-9a-f]{4,40})(?:/[a-zA-Z0-9]+)?$" {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /srv/cgi/gitai-show.pl;
+ fastcgi_param GIT_REPO $1;
+ fastcgi_param GIT_COMMIT $2;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+location ~ "^/gitai-cat-file/([^/]+)/([0-9a-f]{4,40})(?:/[a-zA-Z0-9]+)?$" {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /srv/cgi/gitai-cat-file.pl;
+ fastcgi_param GIT_REPO $1;
+ fastcgi_param GIT_BLOB_ID $2;
+ include fastcgi_params;
+ add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+ expires -1;
+}
+
+#location ~ "^/gitai-raw/([^/]+)/([0-9a-f]{4,40})(?:/r[0-9]+)?$" {
+# rewrite "^/gitai-raw/([^/]+)/([0-9a-f]{4,40})(?:/r[0-9]+)?$" /?p=$1&a=blob_plain&h=$2 break;
+# proxy_pass http://127.0.0.1:80;
+# add_header Cache-Control "no-store, no-cache, must-revalidate" always;
+# expires -1;
+#}
+
+
+ location ~ ^/git(/.*) {
+ fastcgi_pass unix:/var/run/fcgiwrap.socket;
+ fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
+ fastcgi_param GIT_PROJECT_ROOT /var/lib/git;
+ fastcgi_param GIT_HTTP_EXPORT_ALL "";
+ fastcgi_param PATH_INFO $1;
+ include fastcgi_params;
+ }
+
+ location /hooks {
+ proxy_pass http://127.0.0.1:9000/hooks;
+ }
+}