-
-
Notifications
You must be signed in to change notification settings - Fork 346
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
287 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {AppModel, getHomeUrl} from 'app/client/models/AppModel'; | ||
import {bundleChanges, Disposable, Observable} from "grainjs"; | ||
import {ConfigAPI, ConfigAPIImpl} from 'app/common/ConfigAPI'; | ||
|
||
export class ToggleEnterpriseModel extends Disposable { | ||
public readonly edition: Observable<string | null> = Observable.create(this, null); | ||
private readonly _configAPI: ConfigAPI = new ConfigAPIImpl(getHomeUrl()); | ||
constructor (_appModel: AppModel) { | ||
super(); | ||
} | ||
|
||
public async fetchEnterpriseToggle(): Promise<void> { | ||
const edition = await this._configAPI.getValue('edition'); | ||
bundleChanges(() => { | ||
this.edition.set(edition); | ||
}); | ||
} | ||
|
||
public async updateEnterpriseToggle(edition: string): Promise<void> { | ||
await this._configAPI.setValue({edition}, true); | ||
await this.fetchEnterpriseToggle(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import {bigBasicButton, bigBasicButtonLink, bigPrimaryButton} from 'app/client/ui2018/buttons'; | ||
import {theme} from 'app/client/ui2018/cssVars'; | ||
import {styled} from 'grainjs'; | ||
|
||
export const cssSection = styled('div', ``); | ||
|
||
export const cssParagraph = styled('div', ` | ||
color: ${theme.text}; | ||
font-size: 14px; | ||
line-height: 20px; | ||
margin-bottom: 12px; | ||
`); | ||
|
||
export const cssOptInOutMessage = styled(cssParagraph, ` | ||
line-height: 40px; | ||
font-weight: 600; | ||
margin-top: 24px; | ||
margin-bottom: 0px; | ||
`); | ||
|
||
export const cssOptInButton = styled(bigPrimaryButton, ` | ||
margin-top: 24px; | ||
`); | ||
|
||
export const cssOptOutButton = styled(bigBasicButton, ` | ||
margin-top: 24px; | ||
`); | ||
|
||
export const cssSponsorButton = styled(bigBasicButtonLink, ` | ||
margin-top: 24px; | ||
`); | ||
|
||
export const cssButtonIconAndText = styled('div', ` | ||
display: flex; | ||
align-items: center; | ||
`); | ||
|
||
export const cssButtonText = styled('span', ` | ||
margin-left: 8px; | ||
`); | ||
|
||
export const cssSpinnerBox = styled('div', ` | ||
margin-top: 24px; | ||
text-align: center; | ||
`); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import {makeT} from 'app/client/lib/localization'; | ||
import {Computed, Disposable, dom, makeTestId} from "grainjs"; | ||
import {AppModel} from "app/client/models/AppModel"; | ||
import {loadingSpinner} from 'app/client/ui2018/loaders'; | ||
import {commonUrls} from "app/common/gristUrls"; | ||
import {cssLink} from 'app/client/ui2018/links'; | ||
import {ToggleEnterpriseModel} from 'app/client/models/ToggleEnterpriseModel'; | ||
import { | ||
cssOptInButton, | ||
cssOptInOutMessage, | ||
cssOptOutButton, | ||
cssParagraph, | ||
cssSection, | ||
cssSpinnerBox | ||
} from 'app/client/ui/AdminTogglesCss'; | ||
|
||
|
||
const t = makeT('ToggleEnterprsiePage'); | ||
const testId = makeTestId('test-toggle-enterprise-page-'); | ||
|
||
export class ToggleEnterprisePage extends Disposable { | ||
private readonly _model: ToggleEnterpriseModel = new ToggleEnterpriseModel(this._appModel); | ||
private readonly _isEnterprise = Computed.create(this, this._model.edition, (_use, edition) => { | ||
return edition === 'enterprise'; | ||
}) | ||
.onWrite(async (enabled) => { | ||
await this._model.updateEnterpriseToggle(enabled ? 'enterprise' : 'core'); | ||
}); | ||
constructor(private _appModel: AppModel) { | ||
super(); | ||
this._model.fetchEnterpriseToggle().catch(reportError); | ||
} | ||
|
||
public getEnterpriseToggleObservable() { | ||
return this._isEnterprise; | ||
} | ||
|
||
public buildEnterpriseSection() { | ||
return cssSection( | ||
dom.domComputed(this._isEnterprise, isEnterprise => { | ||
if (isEnterprise === null) { | ||
return cssSpinnerBox(loadingSpinner()); | ||
} | ||
return [ | ||
cssParagraph( | ||
t('Activation keys are used to run Grist Enterprise after a trial period ' + | ||
'of 30 days has expired. Get an activation key by signing up for Grist ' + | ||
'Enterprise. You do not need an activation key to run Grist Core.') | ||
), | ||
cssParagraph(t('Learn more in our {{link}}.', { | ||
link: enterpriseHelpCenterLink(), | ||
})), | ||
this._buildEnterpriseSectionButtons(), | ||
]; | ||
}), | ||
testId('enterprise-opt-in-section'), | ||
); | ||
} | ||
|
||
public _buildEnterpriseSectionButtons() { | ||
return dom.domComputed(this._isEnterprise, (enterpriseEnabled) => { | ||
if (enterpriseEnabled) { | ||
return [ | ||
cssOptInOutMessage( | ||
t('Grist Enterprise Edition is enabled.'), | ||
testId('enterprise-opt-out-message'), | ||
), | ||
cssOptOutButton(t('Disable Grist Enterprise Edition'), | ||
dom.on('click', () => this._isEnterprise.set(false)), | ||
), | ||
]; | ||
} else { | ||
return [ | ||
cssOptInButton(t('Enable Grist Enterprise Edition'), | ||
dom.on('click', () => this._isEnterprise.set(true)), | ||
), | ||
]; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
function enterpriseHelpCenterLink() { | ||
return cssLink( | ||
t('Help Center'), | ||
{href: commonUrls.helpEnterpriseOptIn, target: '_blank'}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { BaseAPI, IOptions } from "app/common/BaseAPI"; | ||
import {addCurrentOrgToPath} from 'app/common/urlUtils'; | ||
|
||
export interface ConfigAPI { | ||
getValue(key: string): Promise<any>; | ||
setValue(value: any, restart: boolean): Promise<void>; | ||
} | ||
|
||
export class ConfigAPIImpl extends BaseAPI implements ConfigAPI { | ||
constructor(private _homeUrl: string, options: IOptions = {}) { | ||
super(options); | ||
} | ||
|
||
public async getValue(key: string): Promise<any> { | ||
return (await this.requestJson(`${this._url}/api/config/${key}`, {method: 'GET'})).value; | ||
} | ||
|
||
public async setValue(value: any, restart=false): Promise<void> { | ||
await this.request(`${this._url}/api/config`, { | ||
method: 'PATCH', | ||
body: JSON.stringify({config: value, restart}), | ||
}); | ||
} | ||
|
||
private get _url(): string { | ||
return addCurrentOrgToPath(this._homeUrl); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import * as express from 'express'; | ||
import {expressWrap} from 'app/server/lib/expressWrap'; | ||
|
||
import {IGristCoreConfig} from 'app/server/lib/configCore'; | ||
import {getGlobalConfig} from 'app/server/lib/globalConfig'; | ||
|
||
import log from "app/server/lib/log"; | ||
|
||
export class ConfigBackendAPI { | ||
private _config: IGristCoreConfig; | ||
private static _doRestart = false; | ||
|
||
constructor() { | ||
this._config = getGlobalConfig(); | ||
} | ||
|
||
public addEndpoints(app: express.Express, requireMiddleware: express.RequestHandler) { | ||
app.get('/api/config/:key', requireMiddleware, expressWrap((req, resp) => { | ||
log.debug(`Scheduled to do a config restart is ${ConfigBackendAPI._doRestart}`); | ||
if (ConfigBackendAPI._doRestart) { | ||
resp.on('finish', () => { | ||
// If we have IPC with parent process (e.g. when running under | ||
// Docker) tell the parent that it's time to restart with a | ||
// new config. | ||
if (process.send) { | ||
log.debug('config: requesting supervisor to restart the Grist server'); | ||
process.send({ action: 'restart' }); | ||
} | ||
}); | ||
// We might not have restarted (maybe we aren't running with a | ||
// parent process), but either way, don't try to restart | ||
// again. | ||
ConfigBackendAPI._doRestart = false; | ||
} | ||
log.debug('config: requesting configuration', req.params); | ||
|
||
// Only one key is valid for now | ||
if (req.params.key === 'edition') { | ||
resp.send({value: this._config.edition.get()}); | ||
} else { | ||
resp.status(404).send('Configuration key not found.'); | ||
} | ||
})); | ||
|
||
app.patch('/api/config', requireMiddleware, expressWrap(async (req, resp) => { | ||
|
||
const config = req.body.config; | ||
log.debug('config: received new configuration item', config); | ||
|
||
if (req.body.restart) { | ||
// We do the restart in the GET above, because the frontend | ||
// wants to immediately do a GET after a PATCH, so we still | ||
// need to provide an answer before we restart the server. | ||
ConfigBackendAPI._doRestart = true; | ||
} | ||
|
||
// Only one key is valid for now | ||
if(config.edition !== undefined) { | ||
await this._config.edition.set(config.edition); | ||
|
||
resp.sendStatus(200); | ||
} else { | ||
resp.status(400).send('Invalid configuration key'); | ||
} | ||
})); | ||
} | ||
} |
Oops, something went wrong.