浏览代码

Load global configuration into the frontend as a JS dict using a <script> inclusion

Kailash Nadh 6 年之前
父节点
当前提交
ad8787cab3
共有 7 个文件被更改,包括 41 次插入32 次删除
  1. 30 0
      admin.go
  2. 0 6
      campaigns.go
  3. 2 2
      config.toml.sample
  4. 1 13
      frontend/my/public/index.html
  5. 1 0
      frontend/my/src/App.js
  6. 5 10
      frontend/my/src/Campaign.js
  7. 2 1
      main.go

+ 30 - 0
admin.go

@@ -1,13 +1,43 @@
 package main
 
 import (
+	"bytes"
+	"encoding/json"
 	"net/http"
 
 	"github.com/labstack/echo"
 )
 
+type configScript struct {
+	RootURL    string   `json:"rootURL"`
+	UploadURI  string   `json:"uploadURL"`
+	FromEmail  string   `json:"fromEmail"`
+	Messengers []string `json:"messengers"`
+}
+
 // handleGetStats returns a collection of general statistics.
 func handleGetStats(c echo.Context) error {
 	app := c.Get("app").(*App)
 	return c.JSON(http.StatusOK, okResp{app.Runner.GetMessengerNames()})
 }
+
+// handleGetConfigScript returns general configuration as a Javascript
+// variable that can be included in an HTML page directly.
+func handleGetConfigScript(c echo.Context) error {
+	var (
+		app = c.Get("app").(*App)
+		out = configScript{
+			RootURL:    app.Constants.RootURL,
+			UploadURI:  app.Constants.UploadURI,
+			FromEmail:  app.Constants.FromEmail,
+			Messengers: app.Runner.GetMessengerNames(),
+		}
+
+		b = bytes.Buffer{}
+		j = json.NewEncoder(&b)
+	)
+
+	b.Write([]byte(`var CONFIG = `))
+	j.Encode(out)
+	return c.Blob(http.StatusOK, "application/javascript", b.Bytes())
+}

+ 0 - 6
campaigns.go

@@ -401,12 +401,6 @@ func handleGetRunningCampaignStats(c echo.Context) error {
 	return c.JSON(http.StatusOK, okResp{out})
 }
 
-// handleGetCampaignMessengers returns the list of registered messengers.
-func handleGetCampaignMessengers(c echo.Context) error {
-	app := c.Get("app").(*App)
-	return c.JSON(http.StatusOK, okResp{app.Runner.GetMessengerNames()})
-}
-
 // handleTestCampaign handles the sending of a campaign message to
 // arbitrary subscribers for testing.
 func handleTestCampaign(c echo.Context) error {

+ 2 - 2
config.toml.sample

@@ -3,8 +3,8 @@
 # Interface and port where the app will run its webserver.
 address = "0.0.0.0:9000"
 
-# Root to the listmonk installation that'll be used in the e-mails for linking to
-# images, the unsubscribe URL etc.
+# Public root URL of the listmonk installation that'll be used
+# in the messages for linking to images, unsubscribe page etc.
 root = "http://listmonk.mysite.com"
 
 # The default 'from' e-mail for outgoing e-mail campaigns.

+ 1 - 13
frontend/my/public/index.html

@@ -4,21 +4,9 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta name="theme-color" content="#000000">
-    <!--
-      manifest.json provides metadata used when your web app is added to the
-      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-    -->
+    <script src="%PUBLIC_URL%/api/config.js" type="text/javascript"></script>
     <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
     <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
-    <!--
-      Notice the use of %PUBLIC_URL% in the tags above.
-      It will be replaced with the URL of the `public` folder during the build.
-      Only files inside the `public` folder can be referenced from the HTML.
-
-      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
-      work correctly both with client-side routing and a non-root public URL.
-      Learn how to configure a non-root public URL by running `npm run build`.
-    -->
     <title>React App</title>
   </head>
   <body>

+ 1 - 0
frontend/my/src/App.js

@@ -118,6 +118,7 @@ class App extends React.PureComponent {
                 <Layout modelRequest={ this.modelRequest }
                     request={ this.request }
                     reqStates={ this.state.reqStates }
+                    config={ window.CONFIG }
                     data={ this.state.data } />
             </BrowserRouter>
         )

+ 5 - 10
frontend/my/src/Campaign.js

@@ -298,7 +298,7 @@ class TheFormDef extends React.PureComponent {
                         </Form.Item>
                         <Form.Item {...formItemLayout} label="From address">
                             {getFieldDecorator("from_email", {
-                                initialValue: record.from_email,
+                                initialValue: record.from_email ? record.from_email : this.props.config.fromEmail,
                                 rules: [{ required: true }, { validator: this.validateEmail }]
                             })(<Input disabled={ this.props.formDisabled } placeholder="Company Name <email@company.com>" maxLength="200" />)}
                         </Form.Item>
@@ -325,10 +325,10 @@ class TheFormDef extends React.PureComponent {
                                 <Select disabled={ this.props.formDisabled } mode="tags"></Select>
                                 )}
                         </Form.Item>
-                        <Form.Item {...formItemLayout} label="Messenger">
+                        <Form.Item {...formItemLayout} label="Messenger" style={{ display: this.props.config.messengers.length === 1 ? "none" : "block" }}>
                             {getFieldDecorator("messenger", { initialValue: record.messenger ? record.messenger : "email" })(
                                 <Radio.Group className="messengers">
-                                    {[...this.props.messengers].map((v, i) =>
+                                    {[...this.props.config.messengers].map((v, i) =>
                                         <Radio disabled={ this.props.formDisabled } value={v} key={v}>{ v }</Radio>
                                     )}
                                 </Radio.Group>
@@ -395,7 +395,6 @@ class Campaign extends React.PureComponent {
         campaignID: this.props.route.match.params ? parseInt(this.props.route.match.params.campaignID, 10) : 0,
         record: {},
         contentType: "richtext",
-        messengers: [],
         previewRecord: null,
         body: "",
         currentTab: "form",
@@ -412,14 +411,11 @@ class Campaign extends React.PureComponent {
         // Fetch templates.
         this.props.modelRequest(cs.ModelTemplates, cs.Routes.GetTemplates, cs.MethodGet)
 
-        // Fetch messengers.
-        this.props.request(cs.Routes.GetCampaignMessengers, cs.MethodGet).then((r) => {
-            this.setState({ messengers: r.data.data, loading: false })
-        })
-
         // Fetch campaign.
         if(this.state.campaignID) {
             this.fetchRecord(this.state.campaignID)
+        } else {
+            this.setState({ loading: false })
         }
     }
 
@@ -482,7 +478,6 @@ class Campaign extends React.PureComponent {
                             <TheForm { ...this.props }
                                 record={ this.state.record }
                                 isSingle={ this.state.record.id ? true : false }
-                                messengers={ this.state.messengers }
                                 body={ this.state.body ? this.state.body : this.state.record.body }
                                 contentType={ this.state.contentType }
                                 formDisabled={ this.state.formDisabled }

+ 2 - 1
main.go

@@ -26,6 +26,7 @@ type constants struct {
 	RootURL    string `mapstructure:"root"`
 	UploadPath string `mapstructure:"upload_path"`
 	UploadURI  string `mapstructure:"upload_uri"`
+	FromEmail  string `mapstructure:"from_email"`
 }
 
 // App contains the "global" components that are
@@ -77,6 +78,7 @@ func init() {
 // registerHandlers registers HTTP handlers.
 func registerHandlers(e *echo.Echo) {
 	e.GET("/", handleIndexPage)
+	e.GET("/api/config.js", handleGetConfigScript)
 	e.GET("/api/users", handleGetUsers)
 	e.POST("/api/users", handleCreateUser)
 	e.DELETE("/api/users/:id", handleDeleteUser)
@@ -102,7 +104,6 @@ func registerHandlers(e *echo.Echo) {
 
 	e.GET("/api/campaigns", handleGetCampaigns)
 	e.GET("/api/campaigns/running/stats", handleGetRunningCampaignStats)
-	e.GET("/api/campaigns/messengers", handleGetCampaignMessengers)
 	e.GET("/api/campaigns/:id", handleGetCampaigns)
 	e.GET("/api/campaigns/:id/preview", handlePreviewCampaign)
 	e.POST("/api/campaigns/:id/preview", handlePreviewCampaign)