gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] 01/03: add logic to serve AML SPA


From: gnunet
Subject: [taler-exchange] 01/03: add logic to serve AML SPA
Date: Thu, 16 Nov 2023 15:38:05 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

commit 758afecc1bf42cbfa498f10a9a9735e99758b605
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Thu Nov 16 15:24:24 2023 +0100

    add logic to serve AML SPA
---
 bootstrap                               |  13 ++
 contrib/{Makefile.am => Makefile.am.in} |   8 +
 src/exchange/Makefile.am                |   3 +-
 src/exchange/taler-exchange-httpd.c     |  74 ++++++-
 src/exchange/taler-exchange-httpd_spa.c | 362 ++++++++++++++++++++++++++++++++
 src/exchange/taler-exchange-httpd_spa.h |  49 +++++
 6 files changed, 501 insertions(+), 8 deletions(-)

diff --git a/bootstrap b/bootstrap
index da4f6320..10bd617f 100755
--- a/bootstrap
+++ b/bootstrap
@@ -39,4 +39,17 @@ else
     echo "Uncrustify not detected, hook not installed. Please install 
uncrustify if you plan on doing development"
 fi
 
+
+# Generate Makefile.am in contrib/
+cd contrib
+rm -f Makefile.am
+find wallet-core/aml-backoffice/ -type f -printf '  %p \\\n' | sort > 
Makefile.am.ext
+# Remove extra '\' at the end of the file
+truncate -s -2 Makefile.am.ext
+cat Makefile.am.in Makefile.am.ext >> Makefile.am
+# Prevent accidental editing of the generated Makefile.am
+chmod -w Makefile.am
+cd ..
+
+echo "$0: Running autoreconf"
 autoreconf -fi
diff --git a/contrib/Makefile.am b/contrib/Makefile.am.in
similarity index 84%
rename from contrib/Makefile.am
rename to contrib/Makefile.am.in
index 09e1dcf9..64a5a9f5 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am.in
@@ -61,3 +61,11 @@ EXTRA_DIST = \
   gnunet.tag \
   microhttpd.tag \
   packages
+
+spapkgdatadir = $(prefix)/share/taler/exchange/spa/
+
+# This is for the single-page-app imported from the wallet-core.git
+# prebuilt branch. This MUST be the last line in the
+# Makefile.am.in, as it will be combined with the
+# actual SPA data by 'bootstrap'!
+dist_spapkgdata_DATA = \
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 12ea34e1..7d0e5a33 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -181,8 +181,9 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_reserves_open.c taler-exchange-httpd_reserves_open.h \
   taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
+  taler-exchange-httpd_spa.c taler-exchange-httpd_spa.h \
   taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
-  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h 
+  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h
 
 taler_exchange_httpd_LDADD = \
   $(LIBGCRYPT_LIBS) \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 7c777756..1cde5816 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -68,6 +68,7 @@
 #include "taler-exchange-httpd_reserves_history.h"
 #include "taler-exchange-httpd_reserves_open.h"
 #include "taler-exchange-httpd_reserves_purse.h"
+#include "taler-exchange-httpd_spa.h"
 #include "taler-exchange-httpd_terms.h"
 #include "taler-exchange-httpd_transfers_get.h"
 #include "taler_exchangedb_lib.h"
@@ -1507,6 +1508,56 @@ handle_post_auditors (struct TEH_RequestContext *rc,
 }
 
 
+/**
+ * Generates the response for "/", redirecting the
+ * client to the "/webui/" from where we serve the SPA.
+ *
+ * @param rc request context
+ * @param args remaining arguments (should be empty)
+ * @return MHD result code
+ */
+static MHD_RESULT
+spa_redirect (struct TEH_RequestContext *rc,
+              const char *const args[])
+{
+  const char *text = "Redirecting to /webui/";
+  struct MHD_Response *response;
+
+  response = MHD_create_response_from_buffer (strlen (text),
+                                              (void *) text,
+                                              MHD_RESPMEM_PERSISTENT);
+  if (NULL == response)
+  {
+    GNUNET_break (0);
+    return MHD_NO;
+  }
+  TALER_MHD_add_global_headers (response);
+  GNUNET_break (MHD_YES ==
+                MHD_add_response_header (response,
+                                         MHD_HTTP_HEADER_CONTENT_TYPE,
+                                         "text/plain"));
+  if (MHD_NO ==
+      MHD_add_response_header (response,
+                               MHD_HTTP_HEADER_LOCATION,
+                               "/webui/"))
+  {
+    GNUNET_break (0);
+    MHD_destroy_response (response);
+    return MHD_NO;
+  }
+
+  {
+    MHD_RESULT ret;
+
+    ret = MHD_queue_response (rc->connection,
+                              MHD_HTTP_FOUND,
+                              response);
+    MHD_destroy_response (response);
+    return ret;
+  }
+}
+
+
 /**
  * Handle incoming HTTP request.
  *
@@ -1540,15 +1591,11 @@ handle_mhd_request (void *cls,
       .data = "User-agent: *\nDisallow: /\n",
       .response_code = MHD_HTTP_OK
     },
-    /* Landing page, tell humans to go away. */
+    /* Landing page, redirect to SPA */
     {
       .url = "",
       .method = MHD_HTTP_METHOD_GET,
-      .handler.get = TEH_handler_static_response,
-      .mime_type = "text/plain",
-      .data =
-        "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n",
-      .response_code = MHD_HTTP_OK
+      .handler.get = &spa_redirect
     },
     /* AGPL licensing page, redirect to source. As per the AGPL-license, every
        deployment is required to offer the user a download of the source of
@@ -1778,7 +1825,13 @@ handle_mhd_request (void *cls,
       .handler.post = &handle_post_aml,
       .nargs = 2
     },
-
+    {
+      .url = "webui",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_handler_spa,
+      .nargs = 1,
+      .nargs_is_upper_bound = true
+    },
 
     /* mark end of list */
     {
@@ -2542,6 +2595,13 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  if (GNUNET_OK !=
+      TEH_spa_init ())
+  {
+    global_ret = EXIT_NOTCONFIGURED;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
   if (GNUNET_OK !=
       TALER_TEMPLATING_init ("exchange"))
   {
diff --git a/src/exchange/taler-exchange-httpd_spa.c 
b/src/exchange/taler-exchange-httpd_spa.c
new file mode 100644
index 00000000..66b6fdcf
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_spa.c
@@ -0,0 +1,362 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2020, 2023 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_spa.c
+ * @brief logic to load the single page app (/)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <taler/taler_util.h>
+#include <taler/taler_mhd_lib.h>
+#include <gnunet/gnunet_mhd_compat.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Resource from the WebUi.
+ */
+struct WebuiFile
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct WebuiFile *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct WebuiFile *prev;
+
+  /**
+   * Path this resource matches.
+   */
+  char *path;
+
+  /**
+   * SPA resource, compressed.
+   */
+  struct MHD_Response *zspa;
+
+  /**
+   * SPA resource, vanilla.
+   */
+  struct MHD_Response *spa;
+
+};
+
+
+/**
+ * Resources of the WebuUI, kept in a DLL.
+ */
+static struct WebuiFile *webui_head;
+
+/**
+ * Resources of the WebuUI, kept in a DLL.
+ */
+static struct WebuiFile *webui_tail;
+
+
+MHD_RESULT
+TEH_handler_spa (struct TEH_RequestContext *rc,
+                 const char *const args[])
+{
+  struct WebuiFile *w = NULL;
+  const char *infix = args[0];
+
+  if ( (NULL == infix) ||
+       (0 == strcmp (infix,
+                     "")) )
+    infix = "index.html";
+  for (struct WebuiFile *pos = webui_head;
+       NULL != pos;
+       pos = pos->next)
+    if (0 == strcmp (infix,
+                     pos->path))
+    {
+      w = pos;
+      break;
+    }
+  if (NULL == w)
+    return TALER_MHD_reply_with_error (rc->connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+                                       rc->url);
+  if ( (MHD_YES ==
+        TALER_MHD_can_compress (rc->connection)) &&
+       (NULL != w->zspa) )
+    return MHD_queue_response (rc->connection,
+                               MHD_HTTP_OK,
+                               w->zspa);
+  return MHD_queue_response (rc->connection,
+                             MHD_HTTP_OK,
+                             w->spa);
+}
+
+
+/**
+ * Function called on each file to load for the WebUI.
+ *
+ * @param cls NULL
+ * @param dn name of the file to load
+ */
+static enum GNUNET_GenericReturnValue
+build_webui (void *cls,
+             const char *dn)
+{
+  static struct
+  {
+    const char *ext;
+    const char *mime;
+  } mime_map[] = {
+    {
+      .ext = "css",
+      .mime = "text/css"
+    },
+    {
+      .ext = "html",
+      .mime = "text/html"
+    },
+    {
+      .ext = "js",
+      .mime = "text/javascript"
+    },
+    {
+      .ext = "jpg",
+      .mime = "image/jpeg"
+    },
+    {
+      .ext = "jpeg",
+      .mime = "image/jpeg"
+    },
+    {
+      .ext = "png",
+      .mime = "image/png"
+    },
+    {
+      .ext = "svg",
+      .mime = "image/svg+xml"
+    },
+    {
+      .ext = NULL,
+      .mime = NULL
+    },
+  };
+  int fd;
+  struct stat sb;
+  struct MHD_Response *zspa = NULL;
+  struct MHD_Response *spa;
+  const char *ext;
+  const char *mime;
+
+  (void) cls;
+  /* finally open template */
+  fd = open (dn,
+             O_RDONLY);
+  if (-1 == fd)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              dn);
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      fstat (fd,
+             &sb))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              dn);
+    GNUNET_break (0 == close (fd));
+    return GNUNET_SYSERR;
+  }
+
+  mime = NULL;
+  ext = strrchr (dn, '.');
+  if (NULL == ext)
+  {
+    GNUNET_break (0 == close (fd));
+    return GNUNET_OK;
+  }
+  ext++;
+  for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
+    if (0 == strcasecmp (ext,
+                         mime_map[i].ext))
+    {
+      mime = mime_map[i].mime;
+      break;
+    }
+
+  {
+    void *in;
+    ssize_t r;
+    size_t csize;
+
+    in = GNUNET_malloc_large (sb.st_size);
+    if (NULL == in)
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                           "malloc");
+      GNUNET_break (0 == close (fd));
+      return GNUNET_SYSERR;
+    }
+    r = read (fd,
+              in,
+              sb.st_size);
+    if ( (-1 == r) ||
+         (sb.st_size != (size_t) r) )
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "read",
+                                dn);
+      GNUNET_free (in);
+      GNUNET_break (0 == close (fd));
+      return GNUNET_SYSERR;
+    }
+    csize = (size_t) r;
+    if (MHD_YES ==
+        TALER_MHD_body_compress (&in,
+                                 &csize))
+    {
+      zspa = MHD_create_response_from_buffer (csize,
+                                              in,
+                                              MHD_RESPMEM_MUST_FREE);
+      if (NULL != zspa)
+      {
+        if (MHD_NO ==
+            MHD_add_response_header (zspa,
+                                     MHD_HTTP_HEADER_CONTENT_ENCODING,
+                                     "deflate"))
+        {
+          GNUNET_break (0);
+          MHD_destroy_response (zspa);
+          zspa = NULL;
+        }
+        if (NULL != mime)
+          GNUNET_break (MHD_YES ==
+                        MHD_add_response_header (zspa,
+                                                 MHD_HTTP_HEADER_CONTENT_TYPE,
+                                                 mime));
+      }
+    }
+    else
+    {
+      GNUNET_free (in);
+    }
+  }
+
+  spa = MHD_create_response_from_fd (sb.st_size,
+                                     fd);
+  if (NULL == spa)
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "open",
+                              dn);
+    GNUNET_break (0 == close (fd));
+    if (NULL != zspa)
+    {
+      MHD_destroy_response (zspa);
+      zspa = NULL;
+    }
+    return GNUNET_SYSERR;
+  }
+  if (NULL != mime)
+    GNUNET_break (MHD_YES ==
+                  MHD_add_response_header (spa,
+                                           MHD_HTTP_HEADER_CONTENT_TYPE,
+                                           mime));
+
+  {
+    struct WebuiFile *w;
+    const char *fn;
+
+    fn = strrchr (dn, '/');
+    GNUNET_assert (NULL != fn);
+    w = GNUNET_new (struct WebuiFile);
+    w->path = GNUNET_strdup (fn + 1);
+    w->spa = spa;
+    w->zspa = zspa;
+    GNUNET_CONTAINER_DLL_insert (webui_head,
+                                 webui_tail,
+                                 w);
+  }
+  return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TEH_spa_init ()
+{
+  char *dn;
+
+  {
+    char *path;
+
+    path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
+    if (NULL == path)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    GNUNET_asprintf (&dn,
+                     "%sexchange/spa/",
+                     path);
+    GNUNET_free (path);
+  }
+
+  if (-1 ==
+      GNUNET_DISK_directory_scan (dn,
+                                  &build_webui,
+                                  NULL))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to load WebUI from `%s'\n",
+                dn);
+    GNUNET_free (dn);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_free (dn);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Nicely shut down.
+ */
+void __attribute__ ((destructor))
+get_spa_fini ()
+{
+  struct WebuiFile *w;
+
+  while (NULL != (w = webui_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (webui_head,
+                                 webui_tail,
+                                 w);
+    if (NULL != w->spa)
+    {
+      MHD_destroy_response (w->spa);
+      w->spa = NULL;
+    }
+    if (NULL != w->zspa)
+    {
+      MHD_destroy_response (w->zspa);
+      w->zspa = NULL;
+    }
+    GNUNET_free (w->path);
+    GNUNET_free (w);
+  }
+}
diff --git a/src/exchange/taler-exchange-httpd_spa.h 
b/src/exchange/taler-exchange-httpd_spa.h
new file mode 100644
index 00000000..4147a853
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_spa.h
@@ -0,0 +1,49 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2023 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU General Public License as published by the Free Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_spa.h
+ * @brief logic to preload and serve static files
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_SPA_H
+#define TALER_EXCHANGE_HTTPD_SPA_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Return our single-page-app user interface (see contrib/wallet-core/).
+ *
+ * @param rc context of the handler
+ * @param[in,out] args remaining arguments (ignored)
+ * @return #MHD_YES on success (reply queued), #MHD_NO on error (close 
connection)
+ */
+MHD_RESULT
+TEH_handler_spa (struct TEH_RequestContext *rc,
+                 const char *const args[]);
+
+
+/**
+ * Preload and compress SPA files.
+ *
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TEH_spa_init (void);
+
+
+#endif

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]