Load global configuration into the frontend as a JS dict using a <script> inclusion
This commit is contained in:
parent
09b7fc8d0c
commit
ad8787cab3
30
admin.go
30
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())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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 }
|
||||
|
|
3
main.go
3
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)
|
||||
|
|
Loading…
Reference in New Issue