RRZE-Webteam / rrze-shorturl

WordPress-Plugin to enable a shorturl redirection service run on two servers (WP + PHP-only)
https://www.shorturl.rrze.fau.de
GNU General Public License v3.0
0 stars 1 forks source link

Aufrechterhaltung von ShortURLs von definierten Services gemäß bisherigen Berechnung #2

Closed xwolfde closed 3 months ago

xwolfde commented 7 months ago

Die bisherige Berechnung von ShortURLs erfolgt auf Basis einer Transformation von numerischen Ids auf der Basis 10 (z.b. die Nummer eines OTRS-Tickets) auf Zahlenbasis 37 (!) auf Basis der Zecihenkette abcdefghijklmnopqrstuvwxyz0123456789- . Durch diese mathematische Transformation, die umkehrbar und eindeutig ist, werden auch große Zahlen in sehr kurze Strings umgewandelt.

Die ShortURL besteht aus zwei Bestandteilen: Der Adresse https://go.fau.de/ und dem dem Zeichencode. Der Zeichencode identifiziert eindeutig eine Identifikationsnummer und den dazugehörigen Dienst.

Das erste Zeichen des Zeichencodes identifiziert dabei den zugehörigen Dienst.
Alle folgenden Zeichen sind eine Umrechnung einer oder mehrerer Identifikationsnummern.

Wenn das erste Zeichen keine gültige ID für einen bekannten Service ist (aktuell aktiv nur die "9"), sollen stattdessen zwei Fälle behandelt werden:

Umrechnung von numerischen Ids einer bekannten URL in eine ShortURL-Zeichenkette

Zur Umrechnung einer Identifikationsnummer in den Zeichenstring wird die Nummer zunächst auf die Zahlenbasis 37 umgerechnet. Danach wird jeder Ziffer auf Basis 37 ein Zeichen der Menge abcdefghijklmnopqrstuvwxyz0123456789- zugewiesen. Das Zeichen - nimmt dabei die Rolle des Offsetzeichens an.

Beispiele

ID in einem Dienst Zeichencode
1 a
2 b
37 a-
38 aa
75 ba
50652 1999
50653 a---

Die ShortURL http://go.fau.de/1a wird also umgerechnet stehen für die Adresse des Services mit der Identifikationsnummer 1 und darauf folgend der Id mit dem Wert 1 (a=1).

Buchstaben innerhalb des Zeichenstrings können auch in Goßschrift angegeben werden, sind also Case Insensitiv. Dies ist bewusst gemacht wurden, damit es bei dem Lesen der Shorturls durch einen Menschen weniger Missverständnisse gibt.

Beispiele zur Berechnung auf Basis von bekannten Services, die auf eine numerische Id in der Zieladresse basieren

Wenn ein Ziel für die Erstellung der berechneten URL mehr als eine Id als Parameter benötigt, werden diese Ids bei der ShortURL durch Punkte getrennt.

OTRS (Nur ein Parameter)

Ich hab die lange URL zu einem Ticket mit der TicketID 1167490. Die Target-URL wäre also: https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentTicketZoom;TicketID=1167490

Die ShortURL würde also mit 9 beginnen und danach die Ziffer 1167490 umrechnen. Nach der o.g. Berechnung wäre dies dann: http://go.fau.de/9wa22

Blogdienst (Zwei Parameter in der URL) (derzeit nicht funktionabel, da auf dem Blogdienst die Umleitung auf Instanzen nicht mehr aktiv ist)

Das Webworking-Blog auf dem Blogdienst hat die Instanz-Id 179. Ein Beitrag hat die Post-Id: 142996. Die Target-URL wäre also https://blogs.fau.de/go/179/142996 . (Das ganze würde dann auf https://blogs.fau.de/webworking/?p=142996 umleiten.)

Der ShortURL wird also mit 7 beginnen und die beiden Zahlen durch die Teile 179.142996 berechnen, wobei der Punkt das Trennzeichen ist. Somit sähe in dem Fall die ShortURL so aus: https://go.fau.de/7d4.b3p1

Aktuell wird nur noch der OTRS-Service aktiv genutzt.

Services (ALT)

Folgenden Services waren in der bisherigen Umsetzung definiert. Die Services faq und wke können jedoch ersatzlos entfallen.


$Uniportal::ShortURL::Services = {
        'blog'          => {
                        'title'         => 'Artikel in einem Blog',
                        'kurztitle'     => 'Blogartikel',
                        'servicestarturl'       => 'http://blogs.fau.de',
                        'prefix'        => 7,
                        'targeturl'     => 'http://blogs.fau.de/go/$p1/$p2',                    
                },
        'helpdesk' => {
                        'title'         => 'Helpdesk des RRZE ',
                        'kurztitle'     => 'Helpdesk',                  
                        'servicestarturl' => 'https://www.helpdesk.rrze.fau.de',                                
                        'prefix'                => 9,
                        'targeturl'     =>  'https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentZoom&TicketID=$id',                             
                },                      
        'faq'   => {
                        'title' => 'Fragen und Antworten des RRZE',
                        'kurztitle'     => 'RRZE-FAQs',
                        'servicestarturl'       => 'https://www.faq.rrze.fau.de',
                        'prefix'                => 8,
                        'targeturl'             => 'https://www.helpdesk.rrze.fau.de/otrs/public.pl?Action=PublicFAQ&ItemID=$id',
        },
        'wke' => {
                'title' => 'Webkongress Erlangen',
                'kurztitle' => 'Webkongressartikel',
                'servicestarturl' => 'http://webkongress.fau.de',
                'prefix' => 4,
                'targeturl' => 'http://webkongress.fau.de/?p=$p1',       
        }       

};

Berechnung (Perl)

Bisher wurde die Berechnung mit folgender Perl-Funktion geleistet:

$Uniportal::ShortURL::CONFIG = {
        "ShortURLBase"                                          => "http://go.fau.de/",
        "ShortURLModChars"                                      =>  "abcdefghijklmnopqrstuvwxyz0123456789-",    
};
##############################################################################

sub getIdResourceByServiceURL {
        my $url = shift;
        return if not ($url);           
        $url =~ s/[^a-z0-9\-\?\._\&:;\/\%\$!,\+=]//gi;

        my $key;
        my $serviceurl;
        my $sslserviceurl;
        my $aliasurl;
        my $id;
        my $type;

        foreach $key (keys %{$Uniportal::ShortURL::Services}) {
                $serviceurl = $Uniportal::ShortURL::Services->{$key}->{'targeturl'};
                $serviceurl =~ s/\$id//gi;
                $sslserviceurl =$serviceurl;
                $sslserviceurl =~ s/^http:/https:/gi;
                $aliasurl = '';
                $aliasurl = $Uniportal::ShortURL::Services->{$key}->{'aliasurl'};
                $aliasurl  =~ s/\$id//gi;
                if (($serviceurl) && ($url =~ /$serviceurl([0-9]+)/)) {
                        $id = $1;
                        $type = $key; 
                        last;
                } elsif (($sslserviceurl) && ($url =~ /$sslserviceurl([0-9]+)/)) {
                        $id = $1;
                        $type = $key;
                        last;
                } elsif (($aliasurl) && ($url =~ /$aliasurl([0-9]+)/)) {
                        $id = $1;
                        $type = $key; 
                        last;
                }

        }               
        return $id,$type;
}
##############################################################################
sub createTargetURL {
        my $type = shift;
        my $id = shift;

        if ((not $type) || (not $id)) {
                return;
        }

        my $target = getTargetURLByPrefix($type);

        if (not $target) {
                return;
        }
        $target =~ s/\$id/$id/gi;
        if ($id =~ /\./i)  {
                my @teil = split(/\./,$id);
                my $i;
                my $l;
                my $this;
                my $val;
                for ($i=0; $i<=$#teil; $i++) {
                        $l = $i+1;
                        $this = '$p'.$l;
                        $val = $teil[$i];
                        $target =~ s/\$p$l/$val/gi;
                }

        }
        return $target; 
}
##############################################################################
sub getIdResourceByShortURL {
        my $url = shift;
        my $type;
        my $code;
        my $partcode;
        my $result;

        if ($url =~ /^\/*([0-9])([a-z0-9\-]+)$/i) {
                $type = $1;
                $code = $2;
                $result = calcResource($code);
                return $type,$result;                   
        } elsif ($url =~ /^\/*([0-9])([a-z0-9\-\.]+)$/i) {
                 $type = $1;
                 $partcode = $2;
                my @teil = split(/\./,$partcode);
                my $i;
                for ($i=0; $i<=$#teil; $i++) {
                        $result .= calcResource($teil[$i]);
                        $result .= "." if ($i < $#teil);
                }
                return $type,$result;
        } else {
                return 0,0;
        }
}
##############################################################################
sub calcResource {
        my $code = shift;
        return if (not $code);
        my $modchars = $Uniportal::ShortURL::CONFIG->{'ShortURLModChars'};
        my $modbase = length($modchars);
        my @charlist = split(//,$modchars);

        my @stelle = split(//,$code);
        my $len = length($code);
        my $i;
        my $result;
        my $posval;
        my $thisval;
        my $offset;

        for ($i=$#stelle; $i>=0;$i--) {
                $thisval = posShortURLModChar($stelle[$i]);
                $offset=0;
                $posval = $len - $i -1;
                if ($thisval==$modbase) {
                        $thisval =0;
                } else {
                        $offset = $modbase**$posval;
                        $thisval = $thisval*$offset;
                }
                $result = $result + $thisval;
        }

        return $result;
}
##############################################################################
sub posShortURLModChar {
        my $char = shift;
        my $modchars = $Uniportal::ShortURL::CONFIG->{'ShortURLModChars'};

        my $res =index($modchars,$char);
        if ($res >=0) {
                $res = $res +1;
                return $res;
        } 
        return -1;      
}
##############################################################################
sub getShortURL {
        my $id = shift;
        my $resourcetype = shift;

        my $modchars = $Uniportal::ShortURL::CONFIG->{'ShortURLModChars'};
        my $modbase = length($modchars);        
        my @charlist = split(//,$modchars);

        if ((not $resourcetype) || (not $id)) {
                return; 
        }
        my $prefix = getPrefixByType($resourcetype);
      if (not $prefix) {
        return;
      }  
        my $result;

        if ($id =~ /\./i) {
                my @teil = split(/\./,$id);
                my $i;
                for ($i=0; $i<=$#teil; $i++) {
                        $result .= calcShortURLId($teil[$i]);
                        $result .= "." if ($i<$#teil);
                }
        } else { 
                $result = calcShortURLId($id);
        } 
        my $resurl = $Uniportal::ShortURL::CONFIG->{'ShortURLBase'};
        $resurl .= $prefix.$result; 

        return $resurl;         
}
##############################################################################
sub calcShortURLId {
        my $zahl = shift;
        return if (not $zahl);
        my $modchars = $Uniportal::ShortURL::CONFIG->{'ShortURLModChars'};
        my $modbase = length($modchars);
        my @charlist = split(//,$modchars);

        my $result;
        my $rest;
        my $base = int($zahl/$modbase);
        my $rest = $zahl % $modbase;
        my $leftbase;

                while($base>0) {
                        if ($base >= $modbase) {
                                $leftbase = $base % $modbase;
                                if ($leftbase==0) {
                                        $result = $charlist[$#charlist].$result;
                                } else {
                                        $result = $charlist[$leftbase-1].$result;
                                }
                        }else {
                                $result = $charlist[$base-1].$result;
                        }
                        $base = int($base/$modbase);
                }
                if (($rest==0) && (not $result)) {
                        $result = $charlist[0];
                }
                $result = $result.$charlist[$rest-1];
        return $result;
}
##############################################################################
sub getParamByType {
        my $type = shift;
        return if (not $type);
        my $param = shift;
        my $key;
        foreach $key (keys %{$Uniportal::ShortURL::Services}) {         
                if (lc($key) eq lc($type)) {
                        return $Uniportal::ShortURL::Services->{$key}->{$param};
                }
        }       
}
##############################################################################
sub getPrefixByType {
        my $type = shift;
        return getParamByType($type,"prefix");
}
##############################################################################
sub getTargetURLByType {
        my $type = shift;
        return getParamByType($type,"targeturl");       
}
##############################################################################
sub getTargetURLByPrefix {
        my $prefix = shift;
        my $key;
        foreach $key (keys %{$Uniportal::ShortURL::Services}) {         
                if ($prefix == $Uniportal::ShortURL::Services->{$key}->{'prefix'}) {
                        return $Uniportal::ShortURL::Services->{$key}->{'targeturl'};
                }
        }       
}

Quelldatei: /proj/websource/docs/redirects/www.rrze.fau.info/cgi-bin/lib/Uniportal/ShortURL.pm

xwolfde commented 7 months ago

Die Korrektheit einer neuen Umsetzung in PHP oder JS kann über die folgende URL gegengeprüft werden: http://go.fau.de/shorturl.shtml

thenickless commented 6 months ago

Perl-Code und "Services (ALT)" ist jetzt hier in PHP 8.2 umgesetzt:

https://github.com/RRZE-Webteam/rrze-shorturl/commit/f710e262c9ba9fba78cd973fef11bf97e572bbef

thenickless commented 6 months ago

@xwolfde

Berechnung bekannter URLs ist klar und umgesetzt. Aber wie ist ein URL unserer Domains zu berechnen?

ID vom Dienst sei 1

Kannst du mir das bitte an diesem Bsp sagen?

https://www.fau.de/research/service-fuer-forschende/foerderung-beratung-und-antragsstellung/foerderberatung-und-antragskonzeption/

wird zu https://go.fau.de/1... ?

xwolfde commented 6 months ago

Siehe oben unter Blogdienst. Wir müssten also

  1. Eine Serviceid schaffen als erste Ziffer für den CMS Cluster
  2. dann zwei Ziffern nach der Berechnung, die durch Punkt getrennt sind und einmal die Instanz-ID und einmal die PostID beinhaltet
  3. Dann braucht es auf dem CMS-Cluster das Skript von Rolf aktiv, mit der er dann auf ankommenden Anfragen reagiert und die dann auf die richtige CMS-Seite umleitet.

ABER: Es stellt sich die Frage, ob das so sinnvoll ist. Aufgrund der Länge der beiden IDs plus der führenden Ziffer für die CMS Instanz wird die ShortURL länger als wenn wir eine normale URL speichern und eine ID vergeben. Wir müssten ausserdem je CMS-Cluster (wir haben ja jetzt schon ) unterschiedliche Dienstids haben. Von daher würde ich dazu tendieren, das vielleicht doch nicht so zu machen.

Von daher denke ich, dass es sinnvoller ist, die ZIeladresse normal zu speichern und mit Fall 1 (siehe oben, also eine normale URL ohne Dienstzuweisung) vorzugehen.

thenickless commented 6 months ago

Dann hätten wir nur den Zustand vom alten System, oder? D.h. Kunden-URLs werden gar nicht geshortet.

Aber wir könnten unseren Kunden shorten anbieten. Z.B.:

Bei den Bekannten Diensten ist die Berechnung mit Service-ID, etc logisch. Aber bei unbekannten URLs reicht es doch, dass die unique geshortet werden.

xwolfde commented 6 months ago

Das obige Verfahren liefert kürze ShortURLs als MD5 und co. Deswegen wurde es entwickelt und nicht einfach das bekannte verwendet. Ziel der ShortURLs muss auch sein, dass man die als Dozent an eine Tafel schreiben kann. Oder sich sogar merken kann!

1hg ist leichter merkbar als 8kdhgslkdhgl343hkrz689

thenickless commented 6 months ago

Aus https://www.fau.de/research/service-fuer-forschende/foerderung-beratung-und-antragsstellung/foerderberatung-und-antragskonzeption/ kann nicht der URL in Form von https://www.fau.de/?p=123 ermittelt werden, um nach dem Verfahren zu shorten.

Wie kann "research/service-fuer-forschende/foerderung-beratung-und-antragsstellung/foerderberatung-und-antragskonzeption/" mit dem Verfahren kodiert werden?

xwolfde commented 6 months ago

Speicherung und Generierung:

  1. Wir speichern in der Datenbank des PLugins die komplette URL.
  2. Der Datenbankeintrag hat eine Id.
  3. Diese ID wird nach obigen Verfahren als ShortURL berechnet.

Bei der Rückwärtsauflösung:

  1. Id wird aus der Shorturl berechnet.
  2. In der Datenbank oder einer anderen Table nachgeschaut, welche URL das ist
  3. Dahin umlenken.
thenickless commented 6 months ago

perfekt. danke

thenickless commented 6 months ago

Die Links von unseren Blogs sind nicht in Form von $Uniportal::ShortURL::Services

Erwartet wird http://blogs.fau.de/go/$p1/$p2 Gegeben ist aber zB https://blogs.fau.de/rewi/2024/02/15/erfolgreiches-pilotprojekt-e-klausuren-in-der-uebung-fuer-fortgeschrittene-im-zivilrecht/

Damit funktioniert http://go.fau.de/shorturl.shtml nicht.

(wp_make_link_relative() zu nutzen um andere Formate der URL zu bekommen, funktioniert nur innerhalb der gleichen Website.)

thenickless commented 6 months ago

Im Perl-Code wird nur die Blog-ID, aber nicht auch die Artikel-ID verwendet.

Die Eingabe-URL https://blogs.fau.de/go/179/142996 wird an die Funktion getIdResourceByServiceURL übergeben. Die Funktion durchläuft die im $Uniportal::ShortURL::Services definierten Muster, um eine Übereinstimmung zu finden. Es gleicht die URL mit dem Muster für den Dienst „Blog“ ab, extrahiert die ID 179 und gibt sie zusammen mit dem Diensttyp zurück. Die extrahierte ID 179 wird dann verwendet, um die Ziel-URL für den Dienst „Blog“ zu erstellen, wobei 142996 nicht direkt beteiligt ist. Abschließend wird die Kurz-URL basierend auf der extrahierten ID und dem Diensttyp generiert.

thenickless commented 6 months ago

done in 0.1.3

thenickless commented 4 months ago

Bitte um korrekte Umrechnungsformel von Short-URL zu Long-URL.

Grund: Die Berechnung in Perl ist falsch.

Eingabe: http://go.fau.de/9wa22 Ausgabe: https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentTicketZoom;TicketID=wa22 Soll: https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentTicketZoom;TicketID=1167490

#!/usr/bin/perl

use strict;
use warnings;

package ShortURLConverter;

# Konstruktor-Methode
sub new {
    my $class = shift;
    my $self = {};
    bless $self, $class;
    return $self;
}

# Methode zur Konvertierung der Kurz-URL in die Ziel-URL
sub convertShortURLToTargetURL {
    my ($self, $shortURL) = @_;
    my ($prefix, $code) = $self->getIdResourceByShortURL($shortURL);
    return $self->createTargetURL($prefix, $code);
}

# Methode zur Extrahierung der Präfix- und Codeinformationen aus der Kurz-URL
sub getIdResourceByShortURL {
    my ($self, $url) = @_;
    my ($prefix, $code) = $url =~ m|/([0-9])([a-z0-9\-]+)$|i;
    die "Ungültiges Kurz-URL-Format\n" unless $prefix && $code;
    return ($prefix, $code);
}

# Methode zur Erstellung der Ziel-URL basierend auf dem Präfix und dem Code
sub createTargetURL {
    my ($self, $prefix, $code) = @_;
    my $targetURL = $self->_getTargetURLByPrefix($prefix);
    die "Target-URL nicht gefunden für den angegebenen Präfix\n" unless $targetURL;
    $targetURL =~ s/\$id/$code/g;
    return $targetURL;
}

# Methode zur Abrufung der Ziel-URL anhand des Präfixes (interne Methode)
sub _getTargetURLByPrefix {
    my ($self, $prefix) = @_;
    my %services = (
        '9' => 'https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentTicketZoom;TicketID=$id',
        # Hier weitere Dienste hinzufügen...
    );
    return $services{$prefix};
}

# Ende der ShortURLConverter-Klasse

# Beispielverwendung
my $converter = ShortURLConverter->new();
my $targetURL = $converter->convertShortURLToTargetURL("http://go.fau.de/9wa22");
print $targetURL;

Das gleiche Problem taucht natürlich in der Umsetzung in PHP 8.2 ebenso auf:

<?php

class ShortURLConverter {

    private const ShortURLBase = "http://go.fau.de/";
    private const ShortURLModChars = "abcdefghijklmnopqrstuvwxyz0123456789-";

    private $services = [
        'helpdesk' => [
            'prefix' => 9,
            'targeturl' => 'https://www.helpdesk.rrze.fau.de/otrs/index.pl?Action=AgentTicketZoom;TicketID=$id'
        ]
    ];

    public function convertShortURLToTargetURL($shortURL) {
        try {
            $prefix = substr($shortURL, strlen(self::ShortURLBase), 1);
            $code = $this->getIdResourceByShortURL($shortURL);
            $targetURL = $this->getTargetURLByPrefix($prefix, $code);
            return $targetURL;
        } catch (Exception $e) {
            return $e->getMessage();
        }
    }

    private function getIdResourceByShortURL($url) {
        preg_match('/\/([a-z0-9\-]+)$/i', $url, $matches);
        if (!$matches || count($matches) < 2) {
            throw new Exception('Invalid Short URL format');
        }
        return $matches[1];
    }

    private function getTargetURLByPrefix($prefix, $code) {
        foreach ($this->services as $service) {
            if ($service['prefix'] == $prefix) {
                return str_replace('$id', $code, $service['targeturl']);
            }
        }
        throw new Exception('Target URL not found for given prefix');
    }
}

// Beispielverwendung
$converter = new ShortURLConverter();
$targetURL = $converter->convertShortURLToTargetURL("http://go.fau.de/9wa22");
echo $targetURL;

?>
thenickless commented 4 months ago

Oder sag mir bitte, wie ich auf /proj/websource/docs/redirects/www.rrze.fau.info/cgi-bin/lib/Uniportal/ShortURL.pm zugreifen kann, falls das das File ist, das aktuell beim Helpdesk im Einsatz ist.

xwolfde commented 4 months ago

Hab dir die Files geschickt. WIe anderswo geschrieben ist der Service des Blogdienst nicht mehr relevant und funktioniert daher nicht.

Für OTRS/Helpdesk funktioniert die Umleitung unter obiger URL http://go.fau.de/shorturl.shtml

xwolfde commented 4 months ago

Aktuell erfolgt die Umleitung lt. .htaccess auf go.fau.de so:


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(x0+[0-9A-Za-z\-\.]+)/?$ fau-redirect.php?code=$1 [L]
RewriteRule ^([0-9]+[0-9A-Za-z\-\.]+)/?$ cgi-bin/uniportal/redirect.pl?code=$1 [L,QSA]