2019-07-17 12:28:16 +02:00
const _ = require ( 'lodash' ) ;
2020-05-27 12:47:53 -05:00
const config = require ( '../../../shared/config' ) ;
2020-04-30 20:26:12 +01:00
const errors = require ( '@tryghost/errors' ) ;
2021-10-06 10:42:44 +02:00
const tpl = require ( '@tryghost/tpl' ) ;
2021-06-15 15:36:27 +01:00
const logging = require ( '@tryghost/logging' ) ;
2022-03-10 17:41:46 +00:00
const moment = require ( 'moment' ) ;
2019-07-17 12:28:16 +02:00
const models = require ( '../../models' ) ;
const mail = require ( '../mail' ) ;
2021-10-06 10:42:44 +02:00
const messages = {
setupAlreadyCompleted : 'Setup has already been completed.' ,
setupMustBeCompleted : 'Setup must be completed before making this request.' ,
setupUnableToRun : 'Database missing fixture data. Please reset database and try again.' ,
sampleBlogDescription : 'Thoughts, stories and ideas.' ,
yourNewGhostBlog : 'Your New Ghost Site' ,
2022-02-14 18:22:53 +01:00
unableToSendWelcomeEmail : 'Unable to send welcome email, your site will continue to function.' ,
failedThemeInstall : 'Theme {themeName} didn\'t install because of the error: {error}'
2021-10-06 10:42:44 +02:00
} ;
2022-03-10 17:41:46 +00:00
const postSetupFixtures = {
'coming-soon' : '{"version":"0.3.1","atoms":[],"cards":[],"markups":[["a",["href","#/portal/"]]],"sections":[[1,"p",[[0,[],0,"This is {{site.title}}, a brand new site by {{author.name}} that\'s just getting started. Things will be up and running here shortly, but you can "],[0,[0],1,"subscribe"],[0,[],0," in the meantime if you\'d like to stay up to date and receive emails when new content is published!"]]]],"ghostVersion":"4.0"}' ,
about : '{"version":"0.3.1","atoms":[],"cards":[["hr",{}]],"markups":[["a",["href","https://ghost.org"]]],"sections":[[1,"p",[[0,[],0,"{{site.title}} is an independent publication launched in {{date}} by {{author.name}}. If you subscribe today, you\'ll get full access to the website as well as email newsletters about new content when it\'s available. Your subscription makes this site possible, and allows {{site.title}} to continue to exist. Thank you!"]]],[1,"h3",[[0,[],0,"Access all areas"]]],[1,"p",[[0,[],0,"By signing up, you\'ll get access to the full archive of everything that\'s been published before and everything that\'s still to come. Your very own private library."]]],[1,"h3",[[0,[],0,"Fresh content, delivered"]]],[1,"p",[[0,[],0,"Stay up to date with new content sent straight to your inbox! No more worrying about whether you missed something because of a pesky algorithm or news feed."]]],[1,"h3",[[0,[],0,"Meet people like you"]]],[1,"p",[[0,[],0,"Join a community of other subscribers who share the same interests."]]],[10,0],[1,"h3",[[0,[],0,"Start your own thing"]]],[1,"p",[[0,[],0,"Enjoying the experience? Get started for free and set up your very own subscription business using "],[0,[0],1,"Ghost"],[0,[],0,", the same platform that powers this website."]]]],"ghostVersion":"4.0"}'
} ;
2019-07-17 12:28:16 +02:00
/ * *
* Returns setup status
*
* @ return { Promise < Boolean > }
* /
async function checkIsSetup ( ) {
return models . User . isSetup ( ) ;
}
/ * *
* Allows an assertion to be made about setup status .
*
* @ param { Boolean } status True : setup must be complete . False : setup must not be complete .
* @ return { Function } returns a "task ready" function
* /
function assertSetupCompleted ( status ) {
return async function checkPermission ( _ _ ) {
const isSetup = await checkIsSetup ( ) ;
if ( isSetup === status ) {
return _ _ ;
}
2021-10-06 10:42:44 +02:00
const completed = tpl ( messages . setupAlreadyCompleted ) ;
const notCompleted = tpl ( messages . setupMustBeCompleted ) ;
2019-07-17 12:28:16 +02:00
function throwReason ( reason ) {
2020-04-30 20:26:12 +01:00
throw new errors . NoPermissionError ( { message : reason } ) ;
2019-07-17 12:28:16 +02:00
}
if ( isSetup ) {
throwReason ( completed ) ;
} else {
throwReason ( notCompleted ) ;
}
} ;
}
async function setupUser ( userData ) {
const context = { context : { internal : true } } ;
const owner = await models . User . findOne ( { role : 'Owner' , status : 'all' } ) ;
if ( ! owner ) {
2021-12-01 10:22:01 +00:00
throw new errors . InternalServerError ( {
2021-10-06 10:42:44 +02:00
message : tpl ( messages . setupUnableToRun )
2019-07-17 12:28:16 +02:00
} ) ;
}
const user = await models . User . setup ( userData , _ . extend ( { id : owner . id } , context ) ) ;
return {
user : user ,
userData : userData
} ;
}
async function doSettings ( data , settingsAPI ) {
const context = { context : { user : data . user . id } } ;
const user = data . user ;
const blogTitle = data . userData . blogTitle ;
2022-02-24 09:40:36 -04:00
const description = data . userData . description ? data . userData . description . trim ( ) : null ;
2019-07-17 12:28:16 +02:00
let userSettings ;
if ( ! blogTitle || typeof blogTitle !== 'string' ) {
return user ;
}
userSettings = [
{ key : 'title' , value : blogTitle . trim ( ) } ,
2022-02-24 09:40:36 -04:00
{ key : 'description' , value : description || tpl ( messages . sampleBlogDescription ) }
2019-07-17 12:28:16 +02:00
] ;
2022-02-21 14:55:07 -04:00
if ( data . userData . accentColor ) {
userSettings . push ( {
key : 'accent_color' , value : data . userData . accentColor
} ) ;
}
2019-07-17 12:28:16 +02:00
await settingsAPI . edit ( { settings : userSettings } , context ) ;
return user ;
}
2022-04-12 19:44:21 +05:30
// Update names for default product and newsletter to site title
2022-05-12 19:40:08 +01:00
async function doTiersAndNewsletter ( data , api ) {
const { tiers : tiersAPI , newsletters : newslettersAPI } = api ;
2021-05-09 15:42:50 +05:30
const context = { context : { user : data . user . id } } ;
const user = data . user ;
const blogTitle = data . userData . blogTitle ;
if ( ! blogTitle || typeof blogTitle !== 'string' ) {
return user ;
}
try {
2022-05-12 19:40:08 +01:00
const tierPage = await tiersAPI . browse ( { limit : 'all' } ) ;
2022-04-12 19:44:21 +05:30
const newsletterPage = await newslettersAPI . browse ( { limit : 'all' } ) ;
2022-03-10 10:56:22 +00:00
2022-05-12 19:40:08 +01:00
const defaultTier = tierPage . tiers . find ( p => p . slug === 'default-product' ) ;
2022-04-12 19:44:21 +05:30
const defaultNewsletter = newsletterPage . newsletters . find ( p => p . slug === 'default-newsletter' ) ;
2021-05-09 15:42:50 +05:30
2022-05-12 19:40:08 +01:00
if ( defaultTier ) {
await tiersAPI . edit ( { tiers : [ {
2022-04-12 19:44:21 +05:30
name : blogTitle . trim ( )
2022-05-12 19:40:08 +01:00
} ] } , { context : context . context , id : defaultTier . id } ) ;
2021-05-09 15:42:50 +05:30
}
2022-04-12 19:44:21 +05:30
if ( defaultNewsletter ) {
await newslettersAPI . edit ( { newsletters : [ {
2022-04-14 23:45:59 +05:30
name : blogTitle . trim ( )
2022-04-12 19:44:21 +05:30
} ] } , { context : context . context , id : defaultNewsletter . id } ) ;
}
2021-05-09 15:42:50 +05:30
} catch ( e ) {
return data ;
}
return data ;
}
2022-03-10 17:41:46 +00:00
async function doFixtures ( data ) {
const date = moment ( ) . format ( 'MMMM YYYY' ) ;
_ . each ( postSetupFixtures , async ( mobiledoc , key ) => {
// Using very simple find and replace because we control the fixtures
mobiledoc = mobiledoc . replace ( /{{site.title}}/g , data . userData . blogTitle ) ;
mobiledoc = mobiledoc . replace ( /{{author.name}}/g , data . userData . name ) ;
mobiledoc = mobiledoc . replace ( /{{date}}/ , date ) ;
const post = await models . Post . findOne ( { slug : key } ) ;
2022-04-04 10:45:33 +08:00
if ( post ) {
await models . Post . edit ( { mobiledoc } , { id : post . id } ) ;
}
2022-03-10 17:41:46 +00:00
} ) ;
return data ;
}
2019-07-30 16:15:53 +02:00
function sendWelcomeEmail ( email , mailAPI ) {
2019-07-17 12:28:16 +02:00
if ( config . get ( 'sendWelcomeEmail' ) ) {
2019-07-30 16:13:53 +02:00
const data = {
ownerEmail : email
} ;
2019-07-17 12:28:16 +02:00
return mail . utils . generateContent ( { data : data , template : 'welcome' } )
. then ( ( content ) => {
const message = {
2020-04-29 16:44:27 +01:00
to : email ,
2021-10-06 10:42:44 +02:00
subject : tpl ( messages . yourNewGhostBlog ) ,
2020-04-29 16:44:27 +01:00
html : content . html ,
text : content . text
} ;
const payload = {
mail : [ {
message : message ,
options : { }
} ]
} ;
2019-07-17 12:28:16 +02:00
mailAPI . send ( payload , { context : { internal : true } } )
. catch ( ( err ) => {
2021-10-06 10:42:44 +02:00
err . context = tpl ( messages . unableToSendWelcomeEmail ) ;
2020-04-30 20:26:12 +01:00
logging . error ( err ) ;
2019-07-17 12:28:16 +02:00
} ) ;
2019-07-30 16:13:53 +02:00
} ) ;
2019-07-17 12:28:16 +02:00
}
2019-08-20 15:43:43 +05:30
return Promise . resolve ( ) ;
2019-07-17 12:28:16 +02:00
}
2022-02-03 09:03:59 +01:00
async function installTheme ( data , api ) {
const { theme : themeName } = data . userData ;
if ( ! themeName ) {
return data ;
}
2022-03-02 18:22:20 +01:00
if ( themeName . toLowerCase ( ) === 'tryghost/casper' ) {
logging . warn ( 'Skipping theme install as Casper is the default theme.' ) ;
return data ;
}
2022-02-03 09:03:59 +01:00
// Use the api instead of the services as the api performs extra logic
try {
const installResults = await api . themes . install ( {
source : 'github' ,
ref : themeName ,
context : { internal : true }
} ) ;
const theme = installResults . themes [ 0 ] ;
await api . themes . activate ( {
name : theme . name ,
context : { internal : true }
} ) ;
2022-02-14 18:22:53 +01:00
} catch ( error ) {
2022-02-03 09:03:59 +01:00
//Fallback to Casper by doing nothing as the theme setting update is the last step
2022-02-14 18:22:53 +01:00
logging . warn ( tpl ( messages . failedThemeInstall , { themeName , error : error . message } ) ) ;
2022-02-03 09:03:59 +01:00
}
return data ;
}
2019-07-17 12:28:16 +02:00
module . exports = {
checkIsSetup : checkIsSetup ,
assertSetupCompleted : assertSetupCompleted ,
setupUser : setupUser ,
doSettings : doSettings ,
2022-05-12 19:40:08 +01:00
doProductAndNewsletter : doTiersAndNewsletter ,
2022-03-10 17:41:46 +00:00
installTheme : installTheme ,
doFixtures : doFixtures ,
sendWelcomeEmail : sendWelcomeEmail
2019-07-17 12:28:16 +02:00
} ;