From 80e36ddfa896af1ef280767ccabbb6df5e98bf28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Thu, 7 Sep 2023 14:33:43 +0200 Subject: [PATCH] add basic server to serve GUI files --- .gitignore | 3 +- angular.json | 15 +++- lib/Zonemaster/GUI.pm | 136 +++++++++++++++++++++++++++++ lib/auto/share/dist/Zonemaster-GUI | 1 + package.json | 2 +- scripts/create_manifest.js | 25 ++++++ scripts/zonemaster-gui | 6 ++ share/templates/index.html.tt | 54 ++++++++++++ src/environments/common.ts | 2 +- 9 files changed, 237 insertions(+), 7 deletions(-) create mode 100644 lib/Zonemaster/GUI.pm create mode 120000 lib/auto/share/dist/Zonemaster-GUI create mode 100644 scripts/create_manifest.js create mode 100644 scripts/zonemaster-gui create mode 100644 share/templates/index.html.tt diff --git a/.gitignore b/.gitignore index 05f12ffa..25c33aba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # See http://help.github.com/ignore-files/ for more about ignoring files. # compiled output -/dist +/share/dist +/share/manifest.json /dist-electron /release-builds /tmp diff --git a/angular.json b/angular.json index 14f8c1fb..dcac70dd 100644 --- a/angular.json +++ b/angular.json @@ -39,7 +39,7 @@ "allowedCommonJsDependencies": [ "file-saver" ], - "outputPath": "dist", + "outputPath": "share/dist", "index": "src/index.html", "main": "src/main.ts", "tsConfig": "src/tsconfig.app.json", @@ -49,8 +49,8 @@ "src/.htaccess" ], "styles": [ - "node_modules/fork-awesome/css/fork-awesome.min.css", - "src/styles.scss" + "src/styles.scss", + "node_modules/fork-awesome/css/fork-awesome.min.css" ], "scripts": [], "aot": true, @@ -72,7 +72,14 @@ }, "production": { "localize": true, - "optimization": true, + "optimization": { + "scripts": true, + "styles": { + "minify": true, + "inlineCritical": false + }, + "fonts": true + }, "outputHashing": "all", "sourceMap": false, "namedChunks": false, diff --git a/lib/Zonemaster/GUI.pm b/lib/Zonemaster/GUI.pm new file mode 100644 index 00000000..7fe77bcc --- /dev/null +++ b/lib/Zonemaster/GUI.pm @@ -0,0 +1,136 @@ +package Zonemaster::GUI; + +use strict; +use warnings; + +use Dancer2; +use Carp; +use Data::Dumper; +use I18N::AcceptLanguage; +use TOML::Tiny qw(from_toml); +use File::Slurp qw( read_file ); +use File::ShareDir qw( dist_dir ); + + +our $VERSION = '0.0.0'; + +# Generate configuration +get '/:lang/app.config.json' => sub { + send_as JSON => config->{client_config} +}; + +# Serve additional assets +get '/:lang/additional/**' => sub { + my ( $path ) = splat; + + if ( grep /^\.{2,}$/, @{$path} ) { + pass; + } else { + if ( defined config->{additional_assets_directory} ) { + send_file( path( config->{additional_assets_directory}, @{$path} ), system_path => 1 ); + } + } +}; + +# Serve localized GUI assets, serve index.html for all other requests under /:lang +get '/:lang/?**?' => sub { + my $lang = route_parameters->get('lang'); + my ( $path ) = splat; + my $filename = path( 'dist', $lang, @{$path} ); + + if ( ! grep { $_ eq $lang } @{config->{enabled_languages}} ) { + pass; + } else { + if (-f path( config->{public_dir}, $filename )) { + send_file $filename; + } else { + template 'index.html', { + styles => config->{static_resources}->{styles}, + scripts => config->{static_resources}->{scripts}, + baseurl => join('/', config->{base_url}, $lang, ""), + lang => $lang, + }; + } + } +}; + +# For all other request, redirect based on the browser prefered language +get '/**?' => sub { + my ( $path ) = splat; + my $accept_language = request_header 'Accept-Language'; + my $acceptor = I18N::AcceptLanguage->new( defaultLanguage => config->{default_language}); + my $lang = $acceptor->accepts($accept_language, config->{enabled_languages}); + + redirect uri_for( join('/', config->{base_url}, $lang, @{$path}) ); +}; + +sub build_app { + my $config_file_name = $ENV{ZONEMASTER_GUI_CONFIG_FILE} // "zonemaster-gui.toml"; + my $config_file = read_file $config_file_name; + my $config = from_toml($config_file); + my $install_directory = dist_dir('Zonemaster-GUI'); + + if (! exists $config->{general}->{default_language}) { + croak "Please set `default_language` in configuration file."; + } + + if (! exists $config->{general}->{enabled_languages}) { + croak "Please set `enabled_languages` in configuration file."; + } + + my $base_url = $config->{general}->{base_url} // ''; + + set ( + public_dir => $install_directory, + static_handler => false, + template => 'template_toolkit', + views => path($install_directory, 'templates'), + base_url => $base_url, + default_language => $config->{general}->{default_language}, + enabled_languages => [ @{$config->{general}->{enabled_languages}} ], + ); + + my %client_config; + while( my ($key, $value) = each %{$config->{client}} ) { + $key =~ s/_([[:alpha:]])/\U$1/g; + $client_config{$key} = $value; + } + + $client_config{defaultLanguage} = $config->{general}->{default_language}; + $client_config{enabledLanguages} = $config->{general}->{enabled_languages}; + + set client_config => \%client_config; + + my $manifest_file = read_file path($install_directory, "manifest.json"); + my $static_resources = decode_json($manifest_file); + my %final_static_resources; + + my @additional_styles = map { join('/', 'additional', $_) } @{$config->{customization}->{additional_styles}}; + my @additional_scripts = map { join('/', 'additional', $_) } @{$config->{customization}->{additional_scripts}}; + + my $override_default_style = $config->{general}->{override_default_style} // 0; + + $final_static_resources{styles} = + $override_default_style ? + \@additional_styles : + [ @{$static_resources->{styles}}, @additional_styles ]; + + $final_static_resources{scripts} = [ @{$static_resources->{scripts}}, @additional_scripts ]; + + set ( + static_resources => \%final_static_resources, + additional_assets_directory => $config->{customization}->{additional_assets_directory}, + ); + + if ( exists $config->{customization}->{template_override_directory} ) { + set engines => { + template => { + template_toolkit => { + include_path => $config->{customization}->{template_override_directory} + } + } + } + } + + return to_app; +} diff --git a/lib/auto/share/dist/Zonemaster-GUI b/lib/auto/share/dist/Zonemaster-GUI new file mode 120000 index 00000000..9d936b86 --- /dev/null +++ b/lib/auto/share/dist/Zonemaster-GUI @@ -0,0 +1 @@ +../../../../share \ No newline at end of file diff --git a/package.json b/package.json index 4bce7a86..64219372 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "ng": "ng", "start": "ng serve", - "build": "node scripts/faq.js && ng build --configuration production", + "build": "node scripts/faq.js && ng build --configuration production && node scripts/create_manifest.js ", "release": "npm run build && node scripts/create_release.js", "lint": "ng lint", "e2e": "playwright test", diff --git a/scripts/create_manifest.js b/scripts/create_manifest.js new file mode 100644 index 00000000..4585861f --- /dev/null +++ b/scripts/create_manifest.js @@ -0,0 +1,25 @@ +const fs = require('fs'); + +fs.readdir('./share/dist/en', (err, files) => { + const manifest = { + styles: [], + scripts: [], + }; + + files.forEach(file => { + if (file.endsWith('.css')) { + manifest.styles.push(file); + } else if(file.endsWith('.js')) { + manifest.scripts.push(file); + } + }); + + + fs.writeFile('./share/manifest.json', JSON.stringify(manifest), err => { + if (err) { + console.error(err) + } else { + console.log('manifest.json written') + } + }) + }); diff --git a/scripts/zonemaster-gui b/scripts/zonemaster-gui new file mode 100644 index 00000000..dc2cd125 --- /dev/null +++ b/scripts/zonemaster-gui @@ -0,0 +1,6 @@ +#!/usr/bin/env perl +use Dancer2; +use Zonemaster::GUI; + +Zonemaster::GUI::build_app; +start; diff --git a/share/templates/index.html.tt b/share/templates/index.html.tt new file mode 100644 index 00000000..5adeb477 --- /dev/null +++ b/share/templates/index.html.tt @@ -0,0 +1,54 @@ + + + + + Zonemaster + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [% FOREACH file_name IN styles %] + + [% END %] + + + +
+ Loading +
+
+ + + + +[% FOREACH file_name IN scripts %] + +[% END %] + + diff --git a/src/environments/common.ts b/src/environments/common.ts index 95aff299..e3f281c5 100644 --- a/src/environments/common.ts +++ b/src/environments/common.ts @@ -17,6 +17,6 @@ export const common = { 'sv': 'Svenska' }, enabledLanguages: [ 'da', 'en', 'es', 'fi', 'fr', 'nb', 'sv' ], - configUrl: 'assets/app.config.json', + configUrl: 'app.config.json', pollingInterval: 5 * 1000, }