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

This commit is contained in:
Kailash Nadh 2018-11-02 23:33:00 +05:30
parent 09b7fc8d0c
commit ad8787cab3
7 changed files with 41 additions and 32 deletions

View File

@ -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())
}

View File

@ -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 {

View File

@ -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.

View File

@ -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>

View File

@ -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>
)

View File

@ -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 }

View File

@ -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)