commit af5be2794d12ac52c0baab48e49ac9341c9d1c64 Author: steffen Date: Wed Apr 9 21:43:00 2025 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da6071e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.vscode/ +*/.vscode/ +/.theia/ +*/.theia/ +*/*.deb +*.deb diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/createapp.pl b/createapp.pl new file mode 100644 index 0000000..99f565b --- /dev/null +++ b/createapp.pl @@ -0,0 +1,131 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use Data::Printer; +use File::Find; +use File::Copy; +use Cwd; +use File::Slurp; + +my $secret = `openssl rand -base64 48`; +my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); +$year = $year+1900; + +chomp $secret; + +my $rc = system('git --version'); +if ( $rc < 0) { + print STDOUT "GIT scheint nicht installiert zu sein, Programm wird geschlossen\n"; + exit 0; +} + +print "Wie soll das neue Programm heißen: "; +chomp( my $name = ); + +if ( $name !~ /^[a-z]*$/ ) { + print STDOUT "Programmname muss existieren und darf nur kleinbuchstaben beinhalten, Programm wird geschlossen\n"; + exit 0; +} elsif ( $name eq 'xxx' ) { + print STDOUT "Programmname darf nicht xxx heißen, Programm wird geschlossen\n"; + exit 0; +} + +print "Wohin soll das neue Programm kopiert werden ( $ENV{'HOME'}/mojo ): "; +chomp( my $wohin = ); + +if ( $wohin eq "" ) { + $wohin = "$ENV{'HOME'}/mojo"; +}; +if ( !-d $wohin ) { + print STDOUT "Ordner existiert nicht, Programm wird geschlossen\n"; + exit 0; +} elsif ( $wohin =~ /create_mojo_app/ ) { + print STDOUT "Ordner mit create_mojo_app im Namen sind nicht erlaubt, Programm wird geschlossen\n"; + exit 0; +} elsif ( !-w $wohin ) { + print STDOUT "in Ordner $wohin kann nicht geschrieben werden, Programm wird geschlossen\n"; + exit 0; +} else { + +} +$wohin = $wohin =~ s/\/*$//r; # letztes / entfernen + +my $lname = lcfirst $name; +my $uname = ucfirst $name; + +print "Wofür ist $lname: "; +chomp( my $wofuer = ); + +print "auf welchem Port soll $uname laufen (Default 3000): "; +chomp( my $port = ); +if ( $port eq "" ) { $port = 3000 } + +my $dir = getcwd; + +# Ordner und Dateinamen in xxx durchgehen und XXX mit neuen Appnamen ersetzen +find( \&files, 'xxx' ); + +sub files { + my $str = $File::Find::name; + $str =~ s/Xxx/$uname/g; + $str =~ s/xxx/$lname/g; + + if (-d) { + mkdir $wohin . '/' . $str; + } + else { + copy( $dir . '/' . $File::Find::name, $wohin . '/' . $str ) + or die "The move operation failed: $!"; + } + return; +} + +# Dateien in neuen Appordner durchgehen und Werte mit neuen ersetzen +find( \&newfiles, "$wohin/$lname" ); + +sub newfiles { + + if (-f) { + + #print "File: $dir/$File::Find::name\n"; + if ( $File::Find::name !~ m/.png/g ) { + my @lines = read_file($File::Find::name); + write_file($File::Find::name, ''); + + for my $line (@lines) { + $line =~ s/mojocreateyear/$year/g; + $line =~ s/Xxx/$uname/g; + $line =~ s/xxx/$lname/g; + $line =~ s/mojoappsecret/$secret/g; + $line =~ s/hypnotoadport/$port/g; + $line =~ s/wofuer/$wofuer/g; + append_file($File::Find::name, $line); + } + } + } + return; +} + +# Rechte der Datei ändern + +chmod( 0755, "$wohin/$lname/app/morbo.sh" ); +chmod( 0755, "$wohin/$lname/app/test.sh" ); +chmod( 0755, "$wohin/$lname/deb/DEBIAN/postinst" ); +chmod( 0755, "$wohin/$lname/deb/DEBIAN/prerm" ); +chmod( 0755, "$wohin/$lname/deb/pbuild.sh" ); + +print "App: $uname erstellt\n"; +chdir "$wohin/$lname/"; +system( "git init" ); +system( "git add ." ); +system( "git commit -m 'initialer Commit'" ); +system( "git remote add origin git\@git:WebApps/$uname.git" ); + +system( "$wohin/$lname/deb/pbuild.sh" ) == 0 + or die "Bash Script failed"; +1; + +system( "$wohin/$lname/app/morbo.sh" ) == 0 + or die "Bash Script failed"; +1; diff --git a/xxx/app/lib/XxxApp.pm b/xxx/app/lib/XxxApp.pm new file mode 100644 index 0000000..64c220e --- /dev/null +++ b/xxx/app/lib/XxxApp.pm @@ -0,0 +1,116 @@ +package XxxApp; +use Mojo::Base 'Mojolicious'; +use Time::localtime; + +# This method will run once at server start +sub startup { + my $self = shift; + + # einlesen der lokalen Config + my $config = $self->plugin('Config'); + # $self->plugin('Config', 'file' => $config->{zus_config}); # hier und in der konfig zus_config aktivieren + + $self->secrets([ $config->{secret}]); + + $config->{version} = '__VERSION__'; + $config->{prefix} = ''; + + # Logging mit Log4perl + $self->plugin('steffen::MojoPlug::Syslog' => $config->{logging} ); + $self->app->log->info(sprintf('%s v%s (Perl %s, Mojolicious v%s) started', 'Xxx', $config->{version}, $^V, $Mojolicious::VERSION)); + + # Authentication & Authorization + $self->sessions->cookie_name('Xxx'); + $self->sessions->default_expiration(3600); + $self->sessions->samesite('Strict'); + delete $self->app->static->extra->{'favicon.ico'}; + push @{$self->app->static->paths}, $config->{htlib}; + + # Kompression + $self->app->renderer->compress(1); + + # Authorisation + $self->plugin('steffen::MojoPlug::Authorization' => { verbose => 1 }); + + # Menu über config + $self->plugin('Config', file => 'navigation.conf'); + $self->plugin('steffen::MojoPlug::NavHelper' => { verbose => 1, bs => 5 }); + + # deaktivierte Plugins + # $self->plugin('RenderFile'); + # mysql + # $self->plugin('steffen::MojoPlug::Mysql' => { db => $config->{db} }); + # $config->{test} = $self->dbxxx->{dsn} =~ m/dbs-test/gxms; + + # Local Helpers + $self->plugin('XxxApp::Helpers' => { verbose => 1 }); + + # https://editor.swagger.io/?url=https://petstore.swagger.io/v2/swagger.yaml + $self->plugin('OpenAPI' => { + #route => $self->app->routes->under("/api")->to("api#auth"), + url => $self->app->home->rel_file("openapi.json"), + schema => 'v3', + security => { + BasicAuth => sub { + my ($c, $definition, $scopes, $cb) = @_; + my $haspriv; + for my $sc ( @{$scopes} ) { + $haspriv = 1 if $c->has_priv($sc); + } + + if ( $haspriv ) { + return $c->$cb() + } else { + return $c->$cb('no permission') + } + } + } + }); + + $self->plugin( 'steffen::MojoPlug::SwaggerUI' => { + route => $self->app->routes()->any('/rfc'), + url => "/api", + title => "Xxx", + favicon => "/images/favicon.png" + } + ); + + $self->hook( + 'before_dispatch' => sub { + my $c = shift; + $c->req->url->base->path(''); + $config->{prefix} = ''; + if (!!$c->req->headers->header('Mojo-RP-Location')) { + $c->req->url->base->path( $c->req->headers->header('Mojo-RP-Location') ); + $config->{prefix} = '/'.$c->req->headers->header('Mojo-RP-Location'); + } + } + ); + + # Hook für CORS + $self->hook( + 'after_dispatch' => sub { + my $c = shift; + $c->res->headers->header('Access-Control-Allow-Origin' => '*'); + $c->res->headers->remove('Server'); + } + ); + + # Router + my $r = $self->routes; + + # Normal route to controller + $r->get('/') ->to('main#main', title => 'Xxx'); + $r->get('/jstreetemplate') ->to('menu#jstreetemplate', title => 'JStree Template'); # Beispiel kann gelöscht werden + + $r->get('/xxx') ->to('main#xxx', title => 'Xxx'); + $r->get('/about') ->to('main#about', title => 'Über Xxx'); + $r->get('/login') ->to('main#loginform'); + $r->post('/login') ->to('main#login'); + $r->get('/userinfo') ->to('main#userinfo'); + $r->get('/getoff') ->to('main#getoff'); + + # API über OpenAPI Plugin +} + +1; diff --git a/xxx/app/lib/XxxApp/Controller/Api/Jstreetemplate.pm b/xxx/app/lib/XxxApp/Controller/Api/Jstreetemplate.pm new file mode 100644 index 0000000..38a906a --- /dev/null +++ b/xxx/app/lib/XxxApp/Controller/Api/Jstreetemplate.pm @@ -0,0 +1,376 @@ +package XxxApp::Controller::Api::Jstreetemplate; +use Mojo::Base 'Mojolicious::Controller'; +use Data::Printer; + + my @liste = qw|Anstand +Aufmerksamkeit +Auftrieb +Ausgelassenheit +Ausgewogenheit +Auszeichnung +Bedeutung +Begeisterung +Beglückung +Behaglichkeit +Beherztheit +Belohnung +Bereicherung +Bewunderung +Chance +Charisma +Charme +Dankbarkeit +Daseinsfreude +Ehrlichkeit +Einfallsreichtum +Einigkeit +Empfehlung +Energie +Engagement +Entspannung +Erfolg +Erfüllung +Ermutigung +Faszination +Freiheit +Freude +Freunde +Freundlichkeit +Fröhlichkeit +Fülle +Füreinandereintreten +Gabe +Geduld +Gegenseitigkeit +Gemeinschaftsgeist +Geschenk +Geschlossenheit +Gesundheit +Glück +Glückseligkeit +Glückwunsch +Großzügigkeit +Güte +Harmonie +Hauptgewinn +Heiterkeit +Herzenswärme +Herzensgüte +Herzlichkeit +Hilfsbereitschaft +Hinwendung +Hochgefühl +Hoffnung +Höflichkeit +Idealismus +Intuition +Ideenreichtum +Idylle +Inspiration +Interesse +Jubel +Jugendlichkeit +Kompliment +Köstlichkeit +Kraft +Kreativität +Lächeln +Lachen +Leben +Lebenslust +Lebendigkeit +Lebensfreude +Lebhaftigkeit +Leichtigkeit +Leidenschaft +Liebe +Liebenswürdigkeit +Luxus +Munterkeit +Mut +Nähe +Offenheit +Optimismus +Originalität +Pracht +Prestige +Problemlösung +Pünktlichkeit +Reichhaltigkeit +Reichtum +Respekt +Richtigkeit +Rücksicht +Rücksichtnahme +Schönheit +Schutz +Seelenverwandtschaft +Selbstachtung +Selbstbestimmung +Selbstliebe +Selbstlosigkeit +Selbstsicherheit +Selbstwertgefühl +Selbstzufriedenheit +Seligkeit +Sicherheit +Solidarität +Sorgfalt +Spaß +Spontanität +Stärke +Sympathie +Toleranz +Treue +Überfluss +Überraschung +Überschwang +Überzeugung +Überzeugungskraft +Unbeschwertheit +Unterstützung +Verbesserung +Verbundenheit +Verehrung +Verlockung +Verspieltheit +Verständnis +Vertrauen +Vertrauenswürdigkeit +Vertrautheit +Vertraulichkeit +Vielfalt +Vielseitigkeit +Wahrhaftigkeit +Wärme +Willenskraft +Willkommen +Wohlbefinden +Wohlgefallen +Wohlstand +Wohltat +Wonne +Würde +Wunder +Zauber +Zufriedenheit +Zuneigung +Zusammengehörigkeit +Zusammenhalt +Zuverlässigkeit +Zuwendung|; + +sub jstreedata { + my $c = shift->openapi->valid_input or return; + + my ($src, $tab ) = _generatetestdata(); + + my @tree; + push @tree, @{$src}; + push @tree, @{$tab}; + + return $c->render(json => { rc => 0, msg => 'OK', data => \@tree} ); +} + +sub _generatetestdata { + my ( $liste ) = @_; + my $src; + my $tab; + my $cnt = @liste; + + my $first = int(rand( 10)); + $first ++; + my $second = int(rand( 10)); + $second ++; + my $third = int(rand( 10)); + $third ++; + + for my $f ( 1..$first ) { + my $word = int(rand( $cnt )); + push @{$src}, { parent => '#', id => $f, text => $liste[$word] }; + + for my $s ( 1..$second ) { + my $fs = $f * 10 + $s; + my $wordchild = int(rand( $cnt )); + push @{$tab}, { parent => $f, id => $fs, text => $liste[$wordchild] }; + + for my $t ( 1..$third ) { + my $fst = $fs * 10 + $t; + my $wordchild2 = int(rand( $cnt )); + push @{$tab}, { parent => $fs, id => $fst, text => $liste[$wordchild2] }, + } + } + } + + return ($src, $tab ); +} + +sub datatablesdata { + my $c = shift->openapi->valid_input or return; + my $cnt = @liste; + + my $rows = int(rand( 1000)); + $rows ++; + my @data; + + for my $d ( 0..$rows ) { + my $col1 = int(rand( $cnt )); + my $col2 = int(rand( $cnt )); + my $col3 = int(rand( $cnt )); + my $col4 = int(rand( $cnt )); + my $col5 = int(rand( $cnt )); + my $col6 = int(rand( $cnt )); + my $col7 = int(rand( $cnt )); + my $col8 = int(rand( $cnt )); + my $col9 = int(rand( $cnt )); + + push @data, { col1 => $liste[$col1], col2 => $liste[$col2], col3 => $liste[$col3], col4 => $liste[$col4], col5 => $liste[$col5], col6 => $liste[$col6], col7 => $liste[$col7], col8 => $liste[$col8], col9 => $liste[$col9] }; + } + + + return $c->render(json => { rc => 0, msg => 'OK', data => \@data} ); +} + + +### +sub listfiles { + my $self = shift; + + my $id = $self->stash('todoid'); + + my $sql = "SELECT idma, + size, + created, + info, + type, + name + FROM todo_master_files + WHERE id = ? and deleted = 0"; + + my ( $rc, $msg, $data ) = $self->dbxxx->query_hash_array( $sql, $id ); + + return $self->render( json => { rc => $rc, msg => $msg, data => $data} ); +} + + +sub blob { + my $self = shift; + + my $dbh = $self->dbxxx->get; + return (1, "kein DB-Handle bekommen") if !defined $dbh; + + my $cursor = $dbh->prepare("SELECT file, type FROM todo_master_files where idma = ?"); + $cursor->execute($self->stash('idma')); + my @data = $cursor->fetchrow; + $cursor->finish; + + my $blob; + + if ( defined $data[0]) { + $blob = encode_base64($data[0]); + } else { + $blob = ''; + } + + $self->dbxxx->free($dbh); + return $self->render( json => { blob => $blob, type => $data[1] }); +} + + +sub update { +# update eines eintrages der Mastertabelle + my $self = shift; + + my $todoid = $self->stash('todoid'); + return $self->render( json => { rc => 1, msg => "Die todoid fehlt"} ) if ( $todoid eq "" ) ; + + my $daten = $self->req->json; + + return $self->render( json => { rc => 1, msg => 'Programmname fehlt'} ) if ( !defined $daten->{name} or $daten->{name} eq "" ); + return $self->render( json => { rc => 1, msg => 'Verantwortlichkeit zuweisen'} ) if ( !defined $daten->{who} or $daten->{who} eq "" ); + return $self->render( json => { rc => 1, msg => 'Prozesszuweisung fehlt'} ) if ( !defined $daten->{prozess} or $daten->{prozess} eq "" ); + return $self->render( json => { rc => 1, msg => 'Festellung definieren'} ) if ( !defined $daten->{todoinfo} or $daten->{todoinfo} eq "" ); + return $self->render( json => { rc => 1, msg => 'ext fehlt'} ) if ( !defined $daten->{ext} ); + + my $sql = "UPDATE todo_master set + name = ?, + who = ?, + prozess = ?, + todoinfo = ?, + status = ?, + finishdate = ?, + closed = ?, + notes_master = ?, + targetdate_master = ?, + ext = ? + WHERE id = ?"; + + my ($rc, $msg ) = $self->dbxxx->query_res( $sql, $daten->{'name'}, + $daten->{'who'}, + $daten->{'prozess'}, + $daten->{'todoinfo'}, + $daten->{'status'}, + $daten->{'finishdate'}, + $daten->{'closed'}, + $daten->{'notes_master'}, + $daten->{'targetdate_master'}, + $daten->{'ext'}, + $todoid); + return $self->render(json => { rc => $rc, msg => $msg }); +} + + +sub insert_file { + my $self = shift; + + my $file = $self->param("file"); + my $todoid = $self->param("id"); + my $info = $self->param("info"); + + return $self->render(json => { rc => 1, msg => "Die todoid fehlt"} ) if ( $todoid eq "" ) ; + return $self->render(json => { rc => 1, msg => "Die Info fehlt"} ) if ( $info eq "" ) ; + return $self->render(json => { rc => 1, msg => 'file'}) if (!defined $file->asset->slurp or $file->asset->slurp eq ""); + return $self->render(json => { rc => 1, msg => 'name'}) if (!defined $file->filename or $file->filename eq ""); + return $self->render(json => { rc => 1, msg => 'type'}) if (!defined $file->headers->content_type or $file->headers->content_type eq ""); + return $self->render(json => { rc => 1, msg => 'size'}) if (!defined $file->size or $file->size eq ""); + + my $sql = "INSERT INTO todo_master_files (id, file, type, name, size, info) VALUES (?,?,?,?,?,?)"; + + my ($rc, $msg ) = $self->dbxxx->query_res( $sql, $todoid, $file->asset->slurp, $file->headers->content_type, $file->filename, $file->size, $info ); + + return $self->render(template => 'main/todo_main'); +} + + +sub insert_datei { + my $self = shift; + + my $daten = $self->req->json; + my $id = $self->stash("todoid"); + + return $self->render(json => { rc => 1, msg => "Die ID fehlt"} ) if ( $id eq "" ) ; + my $filebase64 = $daten->{file}; + $filebase64 =~ s/data:$daten->{type};base64//; + + my $file = decode_base64($filebase64); + + my $sql = "INSERT INTO todo_master_files (id, file, type, name, size, info) VALUES (?,?,?,?,?,?)"; + + my ($rc, $msg ) = $self->dbxxx->query_res( $sql, $id, $file, $daten->{type}, $daten->{name}, $daten->{size}, $daten->{info}); + # oder mit der zurückgegebenen id + # my ($rc, $msg, $insid ) = $self->dbkoma->query_inserted_id( $sql, $id, $file, $daten->{type}, $daten->{name}, $daten->{size}, $daten->{info}); + + return $self->render(json => { rc => $rc, msg => $msg }); +} + + +sub deletefile { + my $self = shift; + my $id = $self->stash('idma'); + return $self->render(json => { rc => 1, msg => "Die idma fehlt"}) if ( $id eq "" ) ; + + my ($rc, $msg) = $self->dbxxx->query_res( "DELETE FROM todo_master_files WHERE idma = ?", $id); + return $self->render( json => { rc => $rc, msg => $msg }); +} + +1; diff --git a/xxx/app/lib/XxxApp/Controller/Menu.pm b/xxx/app/lib/XxxApp/Controller/Menu.pm new file mode 100644 index 0000000..b54f2f1 --- /dev/null +++ b/xxx/app/lib/XxxApp/Controller/Menu.pm @@ -0,0 +1,10 @@ +package XxxApp::Controller::Menu; +use Mojo::Base 'Mojolicious::Controller'; + +sub jstreetemplate { + my $c = shift; + + return; +} + +1; diff --git a/xxx/app/lib/XxxApp/Helpers.pm b/xxx/app/lib/XxxApp/Helpers.pm new file mode 100644 index 0000000..4d1daa8 --- /dev/null +++ b/xxx/app/lib/XxxApp/Helpers.pm @@ -0,0 +1,30 @@ +package XxxApp::Helpers; +use Mojo::Base 'Mojolicious::Plugin'; + +sub register { + my ($self, $app, $args) = @_; + + $app->helper('trim' => \&_trim); + $app->helper('debuglog' => \&_debuglog); + + $app->log->info(sprintf('REGISTERED: %s', __PACKAGE__)) if !!$args->{verbose}; + return; +} + +# Führende und nachfolgende Leerzeichen entfernen +sub _trim { + my ($c, $str) = @_; + $str =~ s/^\s+|\s+$//g; + return $str; +}; + +sub _debuglog { + my ($c, $info) = @_; + if ( $c->config->{logging}->{level} =~ /trace|debug/ ) { + $c->log->debug($info); + } + + return 1; +} + +1; diff --git a/xxx/app/lib/XxxApp/Main.pm b/xxx/app/lib/XxxApp/Main.pm new file mode 100644 index 0000000..08b8a03 --- /dev/null +++ b/xxx/app/lib/XxxApp/Main.pm @@ -0,0 +1,53 @@ +package XxxApp::Main; +use Mojo::Base 'Mojolicious::Controller'; + +sub main { + my $self = shift; + return $self->render( template => 'main/main', title => 'Xxx', msg => '' ); +} + + +sub login { + my $self = shift; + + # Grab the request parameters + my $username = $self->param('username'); + my $password = $self->param('password'); + + my $developer = ($username eq 'developer' and $password eq 'wbdfgmnjke834dshf89w7rsdfsdjf' and $self->app->mode =~ /development/xms ) ? 1 : 0; + + if ( $developer or $self->authenticate( $username, $password )) { + $self->session->{msg} = 'Login erfolgreich!'; + push @{$self->session->{privs}}, 'user'; + } else { + $self->logoff(); + $self->session->{url} = '/login'; + $self->session->{msg} = 'Fehler, das Login wurde abgewiesen!'; + } + + return $self->redirect_to( defined $self->session->{url} ? $self->session->{url} : "/" ); +} + +sub loginform { + my $self = shift; + return $self->render( title => 'xxx: Main', msg => '', sess => $self->session); +} + + +sub userinfo { + my $self = shift; + return $self->render( title => 'xxx: Userinfo', sess => $self->session, msg => 'Du besitzt folgende Rechte:'); +} + + +sub getoff { + my $self = shift; + $self->logoff(); + delete $self->session->{privs}; + $self->session->{url} = '/'; + $self->session->{msg} = 'bye, bye ...'; + return $self->redirect_to( "/"); +} + + +1; diff --git a/xxx/app/morbo.sh b/xxx/app/morbo.sh new file mode 100644 index 0000000..c821273 --- /dev/null +++ b/xxx/app/morbo.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd "${0%/*}" +morbo -l http://*:hypnotoadport script/XxxApp -w templates -w lib -w *.conf -w *.json diff --git a/xxx/app/navigation.conf b/xxx/app/navigation.conf new file mode 100644 index 0000000..641d7e4 --- /dev/null +++ b/xxx/app/navigation.conf @@ -0,0 +1,39 @@ +{ + navigation => { + menu => [ + # { + # text => 'QS', + # dropdown => [ + # { text => 'Arbeitsvorrat', href => '/rech/qs' }, + # ] + # }, + # { + # text => 'Recherchen', + # dropdown => [ + # { text => 'Was steht zum Transport bereit?', href => '/rech/dropd' }, + # { text => 'Was wird gerade transportiert?', href => '/rech/taken' }, + # { text => 'Was wurde bereits transportiert?', href => '/rech/done' }, + # ] + # }, + # { + # text => 'Auswertungen', + # dropdown => [ + # { text => 'Transportstatistik', href => '/rept/statistik/0' }, + # { text => 'QS-Statistik', href => '/rept/statistik/1' }, + # ] + # }, + # { + # text => 'Konfiguration', + # priv => 'admin', + # dropdown => [ + # { text => 'Standort festlegen', href => '/conf/stdort' }, + # ] + # }, + { + priv => 'user', + text => 'JSTree Template', + href => '/jstreetemplate', + }, + ], + } +}; diff --git a/xxx/app/openapi.json b/xxx/app/openapi.json new file mode 100644 index 0000000..cb6e5fc --- /dev/null +++ b/xxx/app/openapi.json @@ -0,0 +1,209 @@ +{ + "openapi": "3.0.0", + "servers": [ + { + "url": "/api" + } + ], + "info": { + "version": "1.0", + "title": "Steffens Xxx API" + }, + "paths": { + "/jstreedata": { + "get": { + "security": [ + { + "BasicAuth": [ + "user" + ] + } + ], + "tags": [ + "Jstreetemplate" + ], + "operationId": "jstreedata", + "x-mojo-name": "jstreetemplate_jstreedata", + "x-mojo-to": "api-jstreetemplate#jstreedata", + "responses": { + "200": { + "description": "Daten für das JSTree", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mls_default_response" + } + } + } + } + } + } + }, + "/jstreedataupdate": { + "post": { + "summary": "nicht funktionierende UPDATE Route Controller existiert nicht", + "security": [ + { + "BasicAuth": [ + "user" + ] + } + ], + "tags": [ + "Jstreetemplate" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "required": [ + "id" + ], + "type": "object", + "properties": { + "id": { + "description": "ident zum update", + "type": "integer", + "minimum": 1 + }, + "kat_id": { + "description": "Kategorie ident zum update", + "type": "integer", + "minimum": 1 + }, + "name": { + "description": "Name der Kategorie", + "type": "string", + "minLength": 3, + "maxLength": 20 + } + } + } + } + } + }, + "operationId": "jstreedataupdate", + "x-mojo-name": "jstreetemplate_jstreedataupdate", + "x-mojo-to": "api-jstreetemplate#jstreedataupdate", + "responses": { + "200": { + "description": "Daten für das JSTree", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mls_default_response" + } + } + } + } + } + } + }, + "/jstree/{id}/{kat_id}": { + "get": { + "summary": "nicht funktionierende GET Path Route Controller existiert nicht", + "security": [ + { + "BasicAuth": [ + "user" + ] + } + ], + "tags": [ + "Jstreetemplate" + ], + "parameters": [ + { + "required": true, + "name": "id", + "in": "path", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "required": true, + "name": "kat_id", + "in": "path", + "schema": { + "type": "integer", + "minimum": 1 + } + } + ], + "operationId": "jstree", + "x-mojo-name": "jstreetemplate_jstree", + "x-mojo-to": "api-jstreetemplate#jstree", + "responses": { + "200": { + "description": "Daten für das JSTree", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mls_default_response" + } + } + } + } + } + } + }, + "/datatablesdata": { + "get": { + "security": [ + { + "BasicAuth": [ + "user" + ] + } + ], + "tags": [ + "Jstreetemplate" + ], + "operationId": "datatablesdata", + "x-mojo-name": "jstreetemplate_datatablesdata", + "x-mojo-to": "api-jstreetemplate#datatablesdata", + "responses": { + "200": { + "description": "Daten für die Tabelle", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/mls_default_response" + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "BasicAuth": { + "type": "http", + "scheme": "basic" + } + }, + "schemas": { + "mls_default_response": { + "type": "object", + "properties": { + "rc": { + "type": "integer", + "enum": [ + 0, + 1 + ], + "description": "0 - alles gut | 1 - Fehler" + }, + "msg": { + "type": "string" + } + } + } + } + } +} diff --git a/xxx/app/public/css/jstreetemplate.css b/xxx/app/public/css/jstreetemplate.css new file mode 100644 index 0000000..7f5c86e --- /dev/null +++ b/xxx/app/public/css/jstreetemplate.css @@ -0,0 +1,34 @@ +.jstree-anchor, +.jstree-ocl { + line-height: 20px !important; +} + +.jstree-anchor, +.vakata-context { + font-size: 16px; +} + +.jstree-ocl { + margin-top: -2px !important; +} + +.jstree-icon { + line-height: 16px !important; + transform: scale(1.4) !important; +} + +.jstree-node { + min-height: 20px !important; + line-height: 20px !important; +} + +.treedetail { + border: 0.5px black solid; + overflow: auto; + box-shadow: 0 0 5px #ccc; +} + +.table td, +.table th { + vertical-align: inherit; +} diff --git a/xxx/app/public/css/jstreetemplate.min.css b/xxx/app/public/css/jstreetemplate.min.css new file mode 100644 index 0000000..a2821fe --- /dev/null +++ b/xxx/app/public/css/jstreetemplate.min.css @@ -0,0 +1 @@ +.jstree-anchor,.jstree-ocl{line-height:20px!important}.jstree-anchor,.vakata-context{font-size:16px}.jstree-ocl{margin-top:-2px!important}.jstree-icon{line-height:16px!important;transform:scale(1.4)!important}.jstree-node{min-height:20px!important;line-height:20px!important}.treedetail{border:.5px #000 solid;overflow:auto;box-shadow:0 0 5px #ccc}.table td,.table th{vertical-align:inherit} \ No newline at end of file diff --git a/xxx/app/public/css/xxx.css b/xxx/app/public/css/xxx.css new file mode 100644 index 0000000..9d8ee05 --- /dev/null +++ b/xxx/app/public/css/xxx.css @@ -0,0 +1,23 @@ +body { + width: 100%; + overflow: hidden; + min-height: 0px; + line-height: 1.1; +} + +.navbar { + padding-top: 0px; + padding-bottom: 0px; + height: 50px; +} + +.navbar-brand { + padding-top: 0px; + padding-bottom: 0px; +} + +.footer { + position: fixed; + bottom: 2px; + right: 0px; +} diff --git a/xxx/app/public/css/xxx.min.css b/xxx/app/public/css/xxx.min.css new file mode 100644 index 0000000..c129534 --- /dev/null +++ b/xxx/app/public/css/xxx.min.css @@ -0,0 +1 @@ +body{width:100%;overflow:hidden;min-height:0;line-height:1.1}.navbar{padding-top:0;padding-bottom:0;height:50px}.navbar-brand{padding-top:0;padding-bottom:0}.footer{position:fixed;bottom:2px;right:0} \ No newline at end of file diff --git a/xxx/app/public/images/powered.png b/xxx/app/public/images/powered.png new file mode 100644 index 0000000..5cadcba Binary files /dev/null and b/xxx/app/public/images/powered.png differ diff --git a/xxx/app/public/images/xxx.svg b/xxx/app/public/images/xxx.svg new file mode 100644 index 0000000..91067a4 --- /dev/null +++ b/xxx/app/public/images/xxx.svg @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + SVG + xxx + + + + + + diff --git a/xxx/app/public/js/jstreetemplate.js b/xxx/app/public/js/jstreetemplate.js new file mode 100644 index 0000000..7e50008 --- /dev/null +++ b/xxx/app/public/js/jstreetemplate.js @@ -0,0 +1,174 @@ +var tblmeta, nodeid, nodeselect, tree, table; +var w = { + autoclose: false, + type: 'danger', + top: false, + rc0: { + top: false, + slide: true + } +}; + +var g = { + autoclose: false, + top: false, + rc0: { + top: false, + slide: true + } +}; + +var option = ''; + +function init() { + listtree(); + // JQuery Tooltips aktivieren + $('[data-toggle="tooltip"]').tooltip({ + delay: { + show: 1000, + hide: 100 + } + }); +} + +function listtree() { + $("#tree").jstree("destroy"); + + $.get($('#base_url').val() + '/api/jstreedata/', function (res) { + //if ($.popyrc1(res, w)) return; + $('#tree') + .on('changed.jstree', function (e, data) { + // klick auf einen Node und holen der Mitglieder + if (data.node !== undefined) { + nodeselect = data.node; + treecklick(data); + } + }) + .jstree({ + 'core': { + "check_callback": function (operation, node, node_parent, node_position, more) { + // unterbinden des droppen wenn man nicht owner ist und wenn externe sachen gedropt werden + if (more && more.dnd && operation == 'move_node') { + if (treelock || ogsidstochange[node_parent.id] == undefined) { + return false; + } + return true; + } + }, + "keyboard": false, + "cache": false, + 'data': res.data + }, + "plugins": [ + "wholerow", "sort", "html_data" + ], + }).on('ready.jstree', function (d) { + $('.baum').show(); + }); + + $('.treedetail').css('height', 'calc(100vh - 130px)'); + }); +} + +function treecklick(data) { + $('.words').hide(); + if (data.node.parent != '#') { + $('.words').show(); + gettabledata(data.node.id); + } +} + +function gettabledata() { + + table = $('#tbl-worlds').DataTable({ + ajax: { + "type": "GET", + "url": $('#base_url').val() + '/api/datatablesdata', + dataSrc: function (json) { + return json.data; + } + }, + // fixedHeader: { + // header: true, + // }, + // responsive: { + // details: false + // }, + // scrollCollapse: true, + searching: true, + destroy: true, + deferRender: true, + // createdRow: function (row, data, index) { + // if (data.hidden == 1) { + // $(row).css('opacity', 0.2) + // } + // }, + fixedColumns: { + left: 1, + right: 1 + }, + select: true, + // "stateSave": true, + // "stateLoadParams": function (settings, data) { + // console.log(settings) + // for (var i in data.columns) { + // data.columns[i].regex = false; + // data.columns[i].search = ''; + // } + // }, + columns: [ + { + data: 'col1', + }, + { + data: 'col2', + }, + { + data: 'col3', + }, + { + data: 'col4', + }, + { + data: 'col5', + }, + { + data: 'col6', + }, + { + data: 'col7', + }, + { + data: 'col8', + }, + { + data: 'col9', + } + ], + info: true, + scrollX: true, + // bLengthChange: false, + scroller: true, + scrollY: 'calc(100vh - 320px)', // Höhe der Tabelle + "sDom": 'Bfrti', + language: { + sLoadingRecords: "Lade Wörter...", + sInfo: '_START_ bis _END_ von _TOTAL_ Wörter', + sInfoEmpty: '', + infoFiltered: '(gefiltert von _MAX_ Wörter)', + searchPlaceholder: 'Suche', + sZeroRecords: 'Keine Wörter vorhanden.', + sSearch: '', + oPaginate: { + sFirst: 'Erste', + sPrevious: '<', + sNext: '>', + sLast: 'Letzte' + } + } + }); +} + +$(document).ready(function () { + init(); +}); diff --git a/xxx/app/public/js/jstreetemplate.min.js b/xxx/app/public/js/jstreetemplate.min.js new file mode 100644 index 0000000..b220908 --- /dev/null +++ b/xxx/app/public/js/jstreetemplate.min.js @@ -0,0 +1 @@ +function init(){listtree(),$('[data-toggle="tooltip"]').tooltip({delay:{show:1e3,hide:100}})}function listtree(){$("#tree").jstree("destroy"),$.get($("#base_url").val()+"/api/jstreedata/",function(e){$("#tree").on("changed.jstree",function(e,t){void 0!==t.node&&(nodeselect=t.node,treecklick(t))}).jstree({core:{check_callback:function(e,t,a,o,r){if(r&&r.dnd&&"move_node"==e)return!treelock&&null!=ogsidstochange[a.id]},keyboard:!1,cache:!1,data:e.data},plugins:["wholerow","sort","html_data"]}).on("ready.jstree",function(e){$(".baum").show()}),$(".treedetail").css("height","calc(100vh - 130px)")})}function treecklick(e){$(".words").hide(),"#"!=e.node.parent&&($(".words").show(),gettabledata(e.node.id))}function gettabledata(){table=$("#tbl-worlds").DataTable({ajax:{type:"GET",url:$("#base_url").val()+"/api/datatablesdata",dataSrc:function(e){return e.data}},searching:!0,destroy:!0,deferRender:!0,fixedColumns:{left:1,right:1},select:!0,columns:[{data:"col1"},{data:"col2"},{data:"col3"},{data:"col4"},{data:"col5"},{data:"col6"},{data:"col7"},{data:"col8"},{data:"col9"}],info:!0,scrollX:!0,scroller:!0,scrollY:"calc(100vh - 320px)",sDom:"Bfrti",language:{sLoadingRecords:"Lade Wörter...",sInfo:"_START_ bis _END_ von _TOTAL_ Wörter",sInfoEmpty:"",infoFiltered:"(gefiltert von _MAX_ Wörter)",searchPlaceholder:"Suche",sZeroRecords:"Keine Wörter vorhanden.",sSearch:"",oPaginate:{sFirst:"Erste",sPrevious:"<",sNext:">",sLast:"Letzte"}}})}var tblmeta,nodeid,nodeselect,tree,table,w={autoclose:!1,type:"danger",top:!1,rc0:{top:!1,slide:!0}},g={autoclose:!1,top:!1,rc0:{top:!1,slide:!0}},option="";$(document).ready(function(){init()}); \ No newline at end of file diff --git a/xxx/app/public/js/xxx.js b/xxx/app/public/js/xxx.js new file mode 100644 index 0000000..00d1b35 --- /dev/null +++ b/xxx/app/public/js/xxx.js @@ -0,0 +1,7 @@ +function xxx() { + +} + +$(document).ready(function () { + new xxx(); +}); diff --git a/xxx/app/script/XxxApp b/xxx/app/script/XxxApp new file mode 100644 index 0000000..8f6c8b9 --- /dev/null +++ b/xxx/app/script/XxxApp @@ -0,0 +1,11 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use FindBin; +BEGIN { unshift @INC, "$FindBin::Bin/../lib" } +use Mojolicious::Commands; + +# Start command line interface for application +Mojolicious::Commands->start_app('XxxApp'); diff --git a/xxx/app/t/01_routes.t b/xxx/app/t/01_routes.t new file mode 100644 index 0000000..bf18849 --- /dev/null +++ b/xxx/app/t/01_routes.t @@ -0,0 +1,108 @@ + +print "Test: $0\n"; + +use Mojo::Base -strict; +use Mojo::JSON qw(decode_json); +use Modern::Perl; +use Test::More; +use Test::Mojo; +use DBI; +use Data::Printer; + +#my $dbh = DBI->connect('DBI:mysql:prod:ares', '', '') or die ('Fehler beim Verbinden mit der Datenbank'); +my $t = Test::Mojo->new('XxxApp'); + +$t->app->log->level('info'); +$t->app->ua->max_redirects(1); + +subtest 'prod konfig test' => sub { + my $konf = do '../deb/prod/xxx_app.conf'; + + is exists $konf->{secret}, 1, 'secret = exists'; + is $konf->{htlib}, "/var/local/htlib", 'htlib = /var/local/htlib'; + + is $konf->{hypnotoad}->{pid_file}, "/run/xxx.pid", 'hypnotoad->pid_file = /run/xxx.pid'; + is $konf->{hypnotoad}->{workers}, 4, 'hypnotoad->workers = 4'; + is $konf->{hypnotoad}->{proxy}, 1, 'hypnotoad->proxy = 1'; + + is $konf->{logging}->{enable}, 1, 'logging->enable = 1'; + is $konf->{logging}->{facility}, 'local7', 'logging->facility = local7'; + is $konf->{logging}->{ident}, 'xxx', 'logging->ident = xxx'; + is $konf->{logging}->{level}, 'info', 'logging->level = info'; + is $konf->{logging}->{only_syslog}, 1, 'logging->only_syslog = 1'; +}; + +subtest 'Routes', sub { + note '/'; + $t->get_ok('/') + ->status_is(200) + ->content_like(qr'', 'SVG is availble') + ->header_isnt('Server' => 'Mojolicious'); + + note '/login'; + $t->get_ok('/login') + ->status_is(200) + ->content_like(qr'placeholder="Anmeldename"', 'Anmeldename is availble') + ->content_like(qr'placeholder="Passwort"', 'Passwort is availble'); + + note '/api/jstreedata no permission'; + $t->get_ok('/api/jstreedata') + ->status_is(401); + + note '/api/datatablesdata no permission'; + $t->get_ok('/api/datatablesdata') + ->status_is(401); + + # on this point we have a session + note 'with Login no AD'; + $t->post_ok( + '/login' => form => { username => 'developer', password => 'wbdfgmnjke834dshf89w7rsdfsdjf' } + ); + $t->status_is(302); + + note '/api/jstreedata'; + $t->get_ok('/api/jstreedata') + ->status_is(200) + ->json_is( '/msg' => 'OK', 'msg: OK' ) + ->json_is( '/rc' => 0, 'rc: 0' ); + my $res = $t->tx->res->json; + is ref $res->{data}, 'ARRAY', 'data is ARRAY reference'; + is exists $res->{data}[0]->{id}, 1, 'data has id'; + is exists $res->{data}[0]->{parent}, 1, 'data has firstname'; + is exists $res->{data}[0]->{text}, 1, 'data has name'; + + note '/api/datatablesdata'; + $t->get_ok('/api/datatablesdata') + ->status_is(200) + ->json_is( '/msg' => 'OK', 'msg: OK' ) + ->json_is( '/rc' => 0, 'rc: 0' ); + $res = $t->tx->res->json; + is ref $res->{data}, 'ARRAY', 'data is ARRAY reference'; + is exists $res->{data}[0]->{col1}, 1, 'data has col1'; + is exists $res->{data}[0]->{col2}, 1, 'data has col2'; + is exists $res->{data}[0]->{col3}, 1, 'data has col3'; + is exists $res->{data}[0]->{col4}, 1, 'data has col4'; + is exists $res->{data}[0]->{col5}, 1, 'data has col5'; + is exists $res->{data}[0]->{col6}, 1, 'data has col6'; + is exists $res->{data}[0]->{col7}, 1, 'data has col7'; + is exists $res->{data}[0]->{col8}, 1, 'data has col8'; + is exists $res->{data}[0]->{col9}, 1, 'data has col9'; + is exists $res->{data}[0]->{col10}, '', 'data has no col10'; +}; + +subtest 'POST requestBody required must True', sub { + $t->get_ok('/api'); + $t->status_is(200); + + my $res = $t->tx->res->json; + + for my $path ( keys %{$res->{paths}} ) { + my $pa = $res->{paths}->{$path}; + if ( $pa->{post}->{requestBody} ) { + is $pa->{post}->{requestBody}->{required}, 1, $path; + } + } + +}; + +done_testing(); diff --git a/xxx/app/templates/layouts/index.html.ep b/xxx/app/templates/layouts/index.html.ep new file mode 100644 index 0000000..67e3de5 --- /dev/null +++ b/xxx/app/templates/layouts/index.html.ep @@ -0,0 +1,119 @@ + + + + + + <%= $title %> + + %= stylesheet '/htlib/bootstrap/5.3.3/css/bootstrap.min.css' + %= stylesheet '/htlib/fontawesome/6.4.0/css/all.min.css' + %= stylesheet '/htlib/jquery-datatables/2.1.8/css/dataTables.bootstrap4.min.css' + %= stylesheet '/htlib/jquery-datatables/extensions/Buttons/3.1.2/css/buttons.dataTables.min.css' + %= stylesheet '/htlib/jquery-datatables/extensions/Scroller/2.4.3/css/scroller.bootstrap4.min.css' + %= stylesheet '/htlib/jquery-datatables/extensions/FixedColumns/5.0.3/css/fixedColumns.dataTables.min.css' + %= stylesheet '/htlib/jquery-ui/1.13.2/jquery-ui.min.css' + %= stylesheet '/css/xxx.min.css' + + + + + + + + + + %= javascript '/htlib/jquery/3.7.1/jquery.min.js' + %= javascript '/htlib/jquery-ui/1.13.2/jquery-ui.min.js' + %= javascript '/htlib/bootstrap/5.3.3/js/bootstrap.bundle.min.js' + %= javascript '/htlib/jquery-datatables/2.1.8/js/dataTables.min.js' + %= javascript '/htlib/jquery-datatables/2.1.8/js/dataTables.bootstrap4.min.js' + %= javascript '/htlib/jquery-datatables/extensions/Select/2.1.0/js/dataTables.select.min.js' + %= javascript '/htlib/jquery-datatables/extensions/Select/2.1.0/js/select.bootstrap4.min.js' + %= javascript '/htlib/jquery-datatables/extensions/Buttons/3.1.2/js/dataTables.buttons.min.js' + %= javascript '/htlib/jquery-datatables/extensions/Buttons/3.1.2/js/buttons.colVis.min.js' + %= javascript '/htlib/jquery-datatables/extensions/JSZip/3.10.1/jszip.min.js' + %= javascript '/htlib/jquery-datatables/extensions/pdfmake/0.2.7/pdfmake.min.js' + %= javascript '/htlib/jquery-datatables/extensions/pdfmake/0.2.7/vfs_fonts.js' + %= javascript '/htlib/jquery-datatables/extensions/Buttons/3.1.2/js/buttons.html5.min.js' + %= javascript "/htlib/jquery-datatables/extensions/FixedColumns/5.0.3/js/dataTables.fixedColumns.min.js" + %= javascript '/htlib/jquery-datatables/extensions/Scroller/2.4.3/js/dataTables.scroller.min.js' + %= javascript '/htlib/jquery-datatables/extensions/Scroller/2.4.3/js/scroller.bootstrap4.min.js' + %= javascript '/htlib/jquery-datatables/date-de/date-de.min.js' + + %= javascript "/htlib/mlands/js/popy.min.js" + +<%= content %> + + + + +
+ % use POSIX qw(strftime); + % my $year = strftime('%Y', localtime); +
+ Xxx v<%= $self->config->{version} %>   •   © 2003-<%= $year %> ml&s Gmbh & Co. KG +
+
+ + + diff --git a/xxx/app/templates/main/about.html.ep b/xxx/app/templates/main/about.html.ep new file mode 100644 index 0000000..f8bcee1 --- /dev/null +++ b/xxx/app/templates/main/about.html.ep @@ -0,0 +1,16 @@ +% layout 'index'; + +
+
+

Entwickler:

+

+

Version <%= $self->config->{version} %>

+

powered by

+
+

Mojolicious <%== 'V'.$Mojolicious::VERSION .', Perl '.ucfirst $^V .', ' %>

+

+
+ + diff --git a/xxx/app/templates/main/loginform.html.ep b/xxx/app/templates/main/loginform.html.ep new file mode 100644 index 0000000..4b200b7 --- /dev/null +++ b/xxx/app/templates/main/loginform.html.ep @@ -0,0 +1,35 @@ +% layout 'index'; + +Xxx: Login + +
+%= form_for '/login' => {format => 'txt'} => (method => 'POST', class => 'form-horizontal', id => 'form_login') => begin + +
+
+
+% if ( defined $self->session->{msg} ) { +

+% if ( $self->session->{msg} =~ /Fehler|Sorry/ ) { + +% } else { + +% } + <%= $self->session->{msg} %> + +

+% } +
+
+ +
+
+
+ +
+
+
+
+% end + +
diff --git a/xxx/app/templates/main/main.html.ep b/xxx/app/templates/main/main.html.ep new file mode 100644 index 0000000..b0611ec --- /dev/null +++ b/xxx/app/templates/main/main.html.ep @@ -0,0 +1,16 @@ +% layout 'index'; + + + +
+
+
Willkommen bei
+
+
+ +
+
diff --git a/xxx/app/templates/main/userinfo.html.ep b/xxx/app/templates/main/userinfo.html.ep new file mode 100644 index 0000000..8e52654 --- /dev/null +++ b/xxx/app/templates/main/userinfo.html.ep @@ -0,0 +1,23 @@ +% layout 'index'; + +*Temp*::Userinfo + +
+ +% if ( defined $msg ) { +

+% if ( $msg =~ /Fehler|Sorry/ ) { + +% } else { + +% } + <%= $msg %>

+% } + +% if ( defined $sess->{privs} ) { +% foreach my $s ( @{$sess->{privs} } ) { +

Gruppe: <%= $s %>

+% } +% } + +
diff --git a/xxx/app/templates/menu/jstreetemplate.html.ep b/xxx/app/templates/menu/jstreetemplate.html.ep new file mode 100644 index 0000000..e868388 --- /dev/null +++ b/xxx/app/templates/menu/jstreetemplate.html.ep @@ -0,0 +1,43 @@ +% layout 'index'; + +%= stylesheet '/htlib/jquery-jstree/3.3.17/themes/default/style.min.css' +%= stylesheet '/css/jstreetemplate.min.css' + +
+
+
+ +
+
+ +
+ +%= javascript '/htlib/jquery-jstree/3.3.17/jstree.min.js' +%= javascript "/js/jstreetemplate.min.js?v=". $self->config->{version} diff --git a/xxx/app/test.sh b/xxx/app/test.sh new file mode 100644 index 0000000..02f99cf --- /dev/null +++ b/xxx/app/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +#export PERL5LIB=`pwd` + +if [[ $1 == "all" ]]; then + prove -l t/*.t +else + if [[ -f t/$1.t ]]; then + prove -l -v t/$1.t + else + prove -l -v t/*.t + fi +fi diff --git a/xxx/app/xxx_app.conf b/xxx/app/xxx_app.conf new file mode 100644 index 0000000..d06bd34 --- /dev/null +++ b/xxx/app/xxx_app.conf @@ -0,0 +1,35 @@ +{ + # zus_config => '/etc/mlands/configs/xxx.conf', # um externe konfigs mit Passwörtern zu laden + + htlib => '/var/local/htlib', + mlandshome => 'http://intranet.mlands.com:9673/Z/', + komaurl => 'http://intra/koma', + smtpserver => 'mail.mlands.com', + mail => 'Xxx@mlands.com', + + secret => 'mojoappsecret', + + # DB Pools + # db => { + # xxx => { + # dsn => 'DBI:mysql:test:localhost', + # username => '', + # password => '', + # verbose => 0, + # }, + # }, + + privileges => { + # user => wird bei erfolgreicher Anmeldung den Privs hinzugefügt + }, + + logging => { + level => 'debug', + facility => 'local7', + enable => 1, + only_syslog => 0, + #access_log => 'v1', #bisher nicht änderbar + ident => 'xxx', + #color => 1, #erst ab Mojolicious 9.01 + } +} diff --git a/xxx/deb/DEBIAN/conffiles b/xxx/deb/DEBIAN/conffiles new file mode 100644 index 0000000..f6d376b --- /dev/null +++ b/xxx/deb/DEBIAN/conffiles @@ -0,0 +1 @@ +/opt/xxx/app/xxx_app.conf diff --git a/xxx/deb/DEBIAN/control b/xxx/deb/DEBIAN/control new file mode 100644 index 0000000..a6260e2 --- /dev/null +++ b/xxx/deb/DEBIAN/control @@ -0,0 +1,10 @@ +Package: steffen-xxx +Version: __VERSION__ +Section: unknown +Priority: optional +Architecture: all +Depends: libmojolicious-perl >= (9.39), libev-perl, libsteffen-mojoplug-authorization-perl, libsteffen-mojoplug-navhelper-perl, libsteffen-mojoplug-syslog-perl, libmojolicious-plugin-openapi-perl, libsteffen-mojoplug-swaggerui-perl +Installed-Size: __VERSION__ +Maintainer: Steffen Junge +Description: wofuer + A HTTP Daemon for the steffen xxx Webservice. diff --git a/xxx/deb/DEBIAN/postinst b/xxx/deb/DEBIAN/postinst new file mode 100644 index 0000000..aa833a8 --- /dev/null +++ b/xxx/deb/DEBIAN/postinst @@ -0,0 +1,7 @@ +#!/bin/sh + +# Startskript aktivieren +systemctl daemon-reload +/bin/systemd-sysusers +/bin/systemd-tmpfiles --create +systemctl enable --now steffen-xxx.service diff --git a/xxx/deb/DEBIAN/prerm b/xxx/deb/DEBIAN/prerm new file mode 100644 index 0000000..5cf6285 --- /dev/null +++ b/xxx/deb/DEBIAN/prerm @@ -0,0 +1,6 @@ +#!/bin/sh +# $Id$ + +# Daemon stoppen und deaktivieren +systemctl stop steffen-xxx.service || true +systemctl disable steffen-xxx.service || true diff --git a/xxx/deb/pbuild.sh b/xxx/deb/pbuild.sh new file mode 100644 index 0000000..f1c52e0 --- /dev/null +++ b/xxx/deb/pbuild.sh @@ -0,0 +1,52 @@ +#! /bin/sh +cd "${0%/*}" || exit +SCRIPTPATH=$(pwd) +cd ../app || exit +rc=0 +prove -l -v t/*.t +rc=$? +cd "$SCRIPTPATH" || exit + +if [ $rc -eq 0 ] ; then + # Jahr in dem das Programm begonnen wurde + createyear=mojocreateyear + currentyear=$(date +'%Y') + major=$((currentyear-createyear)) + minor=$(date +'%m') + + progpfad="opt/xxx/" + apppfad="app/lib/XxxApp.pm" + workdir=/tmp/$USER_$(pwd | sed 's/^.*\///') + rm -Rf "${workdir:?}/*" + mkdir -p "$workdir" + # Daten in workdir sysnchronisieren + rsync -Ca ./DEBIAN "$workdir/" + rsync -Ca ./usr "$workdir/" + mkdir -p "$workdir/opt" + rsync -Ca ../app "$workdir/$progpfad" + + # Test entfernen + rm -r "$workdir/$progpfad/app/t" + rm "$workdir/$progpfad/app/test.sh" + + # config aus prod ordner kopieren + cp -ar ./prod/* "$workdir/$progpfad/app" + + # Revision holen + REV=$(git rev-list --count --all) + VER="$major.$minor.$REV" + + # Version in die startup app.pm und control eintragen + sed -i "s@__VERSION__@$VER@g" "$workdir/DEBIAN/control" + sed -i "s@__VERSION__@$VER@g" "$workdir/$progpfad/$apppfad" + + SIZE=$(du -h -k --max-depth=0 "$workdir" | cut -d '/' -f1) + sed -i "s@__SIZE__@$SIZE@g" "$workdir/DEBIAN/control" + echo "Version: $VER, Size: $SIZE" + + chmod -R g-s "$workdir" + # paket erstellen + fakeroot dpkg-deb -b "$workdir" ./ +else + echo "TESTs failed no package created" +fi diff --git a/xxx/deb/prod/xxx_app.conf b/xxx/deb/prod/xxx_app.conf new file mode 100644 index 0000000..50ecac4 --- /dev/null +++ b/xxx/deb/prod/xxx_app.conf @@ -0,0 +1,40 @@ +{ + # zus_config => '/etc/mlands/configs/xxx.conf', # um externe konfigs mit Passwörtern zu laden + + htlib => '/var/local/htlib', + + secret => 'mojoappsecret', + + # dieser Teil wandert in die Datei unter zus_config + # DB Pools + # db => { + # xxx => { + # dsn => 'DBI:mysql:test:ares', + # username => '', + # password => '', + # verbose => 0, + # }, + # }, + + hypnotoad => { + listen => ['http://*:hypnotoadport'], + workers => 4, + pid_file => '/run/xxx.pid', + proxy => 1 + }, + + privileges => { + # user => wird bei erfolgreicher Anmeldung den Privs hinzugefügt + # admin => 'xxx_admin::xgroups', + }, + + logging => { + level => 'info', + facility => 'local7', + enable => 1, + only_syslog => 1, + #access_log => 'v1', #bisher nicht änderbar + ident => 'xxx', + #color => 1, #erst ab Mojolicious 9.01 + } +} diff --git a/xxx/deb/usr/lib/systemd/system/steffen-xxx.service b/xxx/deb/usr/lib/systemd/system/steffen-xxx.service new file mode 100644 index 0000000..9e95755 --- /dev/null +++ b/xxx/deb/usr/lib/systemd/system/steffen-xxx.service @@ -0,0 +1,41 @@ +[Unit] +Description=steffen Xxx/Rfs Webservice +After=network.target syslog.socket +StartLimitBurst=5 +StartLimitIntervalSec=10 + +[Service] +Type=forking +User=steffen-xxx +Group=webapps +WorkingDirectory=/opt/xxx/app +ExecStart=/usr/bin/hypnotoad ./script/XxxApp +ExecReload=/usr/bin/hypnotoad ./script/XxxApp +Restart=on-failure +PIDFile=/run/xxx.pid +KillMode=process + +# Optional hardening to improve security +ReadWritePaths=/opt/xxx +NoNewPrivileges=yes +MemoryDenyWriteExecute=true +PrivateDevices=yes +PrivateTmp=yes +ProtectHome=yes +#ProtectSystem=strict +ProtectControlGroups=true +#RestrictSUIDSGID=true +RestrictRealtime=true +LockPersonality=true +#ProtectKernelLogs=true +ProtectKernelTunables=true +#ProtectHostname=true +ProtectKernelModules=true +PrivateUsers=true +#ProtectClock=true +SystemCallArchitectures=native +SystemCallErrorNumber=EPERM +SystemCallFilter=@system-service + +[Install] +WantedBy=multi-user.target diff --git a/xxx/deb/usr/lib/sysusers.d/steffen-xxx.conf b/xxx/deb/usr/lib/sysusers.d/steffen-xxx.conf new file mode 100644 index 0000000..dd05ae8 --- /dev/null +++ b/xxx/deb/usr/lib/sysusers.d/steffen-xxx.conf @@ -0,0 +1,5 @@ +# see https://www.freedesktop.org/software/systemd/man/latest/sysusers.d.html +# Type Name ID GECOS Home Shell +g webapps - - +u steffen-xxx - "steffen Xxx" /opt/xxx - +m steffen-xxx webapps diff --git a/xxx/deb/usr/lib/tmpfiles.d/steffen-xxx.conf b/xxx/deb/usr/lib/tmpfiles.d/steffen-xxx.conf new file mode 100644 index 0000000..b9fe480 --- /dev/null +++ b/xxx/deb/usr/lib/tmpfiles.d/steffen-xxx.conf @@ -0,0 +1,3 @@ +# see https://www.freedesktop.org/software/systemd/man/latest/tmpfiles.d.html +# Type Path Mode User Group Age Argument +Z /opt/xxx - steffen-xxx steffen-xxx