feat(maildev): auto-configure instances with maildev settings

This commit is contained in:
Ben Allfree 2024-06-28 06:59:12 -07:00
parent e4d35aeea3
commit 14b951ac44
6 changed files with 497 additions and 3 deletions

View File

@ -0,0 +1,5 @@
---
'@pockethost/plugin-maildev': patch
---
At launch, instances are configured to use maildev settings

View File

@ -1,6 +1,6 @@
{ {
"name": "@pockethost/plugin-maildev", "name": "@pockethost/plugin-maildev",
"version": "1.0.0", "version": "0.0.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "http://github.com/pockethost/pockethost/packages/plugin-maildev" "url": "http://github.com/pockethost/pockethost/packages/plugin-maildev"
@ -23,6 +23,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"commander": "^11.1.0", "commander": "^11.1.0",
"immer": "^10.1.1",
"maildev": "^2.1.0" "maildev": "^2.1.0"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -1,22 +1,30 @@
import { join } from 'path' import { dirname, join } from 'path'
import { DEBUG } from 'pockethost' import { DEBUG } from 'pockethost'
import { import {
PH_HOME, PH_HOME,
Settings, Settings,
logSettings, logSettings,
mkBoolean,
mkNumber, mkNumber,
mkPath, mkPath,
} from 'pockethost/core' } from 'pockethost/core'
import { fileURLToPath } from 'url'
export const PLUGIN_NAME = `plugin-maildev` export const PLUGIN_NAME = `plugin-maildev`
export const HOME_DIR = export const HOME_DIR =
process.env.PH_MAILDEV_HOME || join(PH_HOME(), PLUGIN_NAME) process.env.PH_MAILDEV_HOME || join(PH_HOME(), PLUGIN_NAME)
const __dirname = dirname(fileURLToPath(import.meta.url))
export const PROJECT_DIR = (...paths: string[]) =>
join(__dirname, '..', ...paths)
const settings = Settings({ const settings = Settings({
PH_MAILDEV_HOME: mkPath(HOME_DIR, { create: true }), PH_MAILDEV_HOME: mkPath(HOME_DIR, { create: true }),
PH_MAILDEV_SMTP_PORT: mkNumber(1025), PH_MAILDEV_SMTP_PORT: mkNumber(1025),
PH_MAILDEV_WEB_ADMIN_PORT: mkNumber(1080), PH_MAILDEV_WEB_ADMIN_PORT: mkNumber(1080),
PH_MAILDEV_ALWAYS_USE_LOCAL: mkBoolean(true),
}) })
export const PORT = () => settings.PH_MAILDEV_SMTP_PORT export const PORT = () => settings.PH_MAILDEV_SMTP_PORT

View File

@ -1,13 +1,15 @@
import { Command } from 'commander' import { Command } from 'commander'
import { produce } from 'immer'
import MailDev from 'maildev' import MailDev from 'maildev'
import { import {
IS_DEV, IS_DEV,
PocketHostPlugin, PocketHostPlugin,
onCliCommandsFilter, onCliCommandsFilter,
onInstanceConfigFilter,
onServeAction, onServeAction,
onServeSlugsFilter, onServeSlugsFilter,
} from 'pockethost' } from 'pockethost'
import { PLUGIN_NAME, PORT, WEB_ADMIN_PORT } from './constants' import { PLUGIN_NAME, PORT, PROJECT_DIR, WEB_ADMIN_PORT } from './constants'
import { dbg } from './log' import { dbg } from './log'
const serve = () => { const serve = () => {
@ -44,6 +46,16 @@ const plugin: PocketHostPlugin = async ({}) => {
// } // }
// }) // })
onInstanceConfigFilter(async (config) => {
return produce(config, (draft) => {
draft.env.PH_MAILDEV_PORT = PORT().toString()
draft.binds.hooks.push({
src: PROJECT_DIR(`src/instance-app/hooks/**/*`),
base: PROJECT_DIR(`src/instance-app/hooks`),
})
})
})
onCliCommandsFilter(async (commands) => { onCliCommandsFilter(async (commands) => {
return [...commands, MailDevCommand()] return [...commands, MailDevCommand()]
}) })

View File

@ -0,0 +1,139 @@
/// <reference path="../../../../pockethost/src/instance-app/types/all.d.ts" />
$app.onBeforeServe().add((e) => {
return
const dao = $app.dao()
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/_ph_lib.js`))
const log = mkLog(`plugin-maildev`)
log(`Starting plugin-maildev`)
const APP_DEFAULTS = {
appName: 'Acme',
appUrl: 'http://localhost:8090',
senderName: 'Support',
senderAddress: 'support@example.com',
}
try {
const settings = dao.findSettings()
if (!settings) {
throw new Error(`Expected settings here`)
}
const fix = (field, newValue) => {
if (!newValue || settings.meta[field] !== APP_DEFAULTS[field]) return
settings.meta[field] = newValue
}
fix(`appName`, PH_APP_NAME)
fix(`appUrl`, PH_INSTANCE_URL)
fix(`senderName`, PH_APP_NAME)
fix(`senderAddress`, `${PH_APP_NAME}@app.pockethost.io`)
dao.saveSettings(settings)
log(`***defaults successfully applied`)
} catch (e) {
log(`***error applying defaults: ${e}`)
}
})
$app.onBeforeServe().add((e) => {
return
const dao = $app.dao()
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/_ph_lib.js`))
const log = mkLog(`plugin-maildev`)
log(`Starting plugin-maildev`)
const { id, email, tokenKey, passwordHash } = (() => {
try {
return /** @type{{id:string, email:string, tokenKey:string,passwordHash:string}} */ (
JSON.parse($os.getenv(`ADMIN_SYNC`))
)
} catch (e) {
return { id: '', email: '', tokenKey: '', passwordHash: '' }
}
})()
if (!email) {
log(`Not active - skipped`)
return
}
const result = new DynamicModel({
// describe the shape of the data (used also as initial values)
id: '',
})
try {
dao
.db()
.newQuery('SELECT * from _admins where email = {:email}')
.bind({ email })
.one(result)
log(
`Existing admin record matching PocketHost login found - updating with latest credentials`,
)
try {
dao
.db()
.newQuery(
'update _admins set tokenKey={:tokenKey}, passwordHash={:passwordHash} where email={:email}',
)
.bind({ email, tokenKey, passwordHash })
.execute()
log(`Success`)
} catch (e) {
log(`Failed to update admin credentials: ${e}`)
}
} catch (e) {
log(`No admin record matching PocketHost credentials - creating`)
try {
dao
.db()
.newQuery(
'insert into _admins (id,email, tokenKey, passwordHash) VALUES ({:id}, {:email}, {:tokenKey}, {:passwordHash})',
)
.bind({
id,
email,
tokenKey,
passwordHash,
created: new Date().toISOString(),
updated: new Date().toISOString(),
})
.execute()
log(`Success`)
} catch (e) {
log(`Failed to insert admin credentials: ${e}`)
}
}
})
$app.onBeforeServe().add((e) => {
const dao = $app.dao()
const { mkLog } = /** @type {Lib} */ (require(`${__hooks}/_ph_lib.js`))
const log = mkLog(`plugin-maildev`)
log(`Starting plugin-maildev`)
try {
const settings = dao.findSettings()
if (!settings) {
throw new Error(`Expected settings here`)
}
settings.smtp[`enabled`] = true
settings.smtp[`host`] = `localhost`
settings.smtp[`port`] = $os.getenv(`PH_MAILDEV_PORT`)
settings.smtp[`tls`] = false
dao.saveSettings(settings)
log(`***defaults successfully applied`)
} catch (e) {
log(`***error applying defaults: ${e}`)
}
})

View File

@ -0,0 +1,329 @@
{
"adminAuthToken": {
"duration": 1209600,
"secret": "TxmGmMf91xixFDppollsByz7gc0kEgNPGR4MD4lMBCBxmYLKh7"
},
"adminFileToken": {
"duration": 120,
"secret": "BrYUNzmQ6819CY9LqfcTFFZ3v6bMs34aZxAmjym1BxqiaUmMNJ"
},
"adminPasswordResetToken": {
"duration": 1800,
"secret": "ODnVe9kQaR0PByFTtpILOTFJNzooaMyFTQh2jJRbUxD31N280T"
},
"appleAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"backups": {
"cron": "0 0 * * *",
"cronMaxKeep": 3,
"s3": {
"accessKey": "",
"bucket": "",
"enabled": false,
"endpoint": "",
"forcePathStyle": false,
"region": "",
"secret": ""
}
},
"discordAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"emailAuth": {
"enabled": true,
"exceptDomains": null,
"minPasswordLength": 8,
"onlyDomains": null
},
"facebookAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"giteaAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"giteeAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"githubAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"gitlabAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"googleAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"instagramAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"kakaoAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"livechatAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"logs": {
"logIp": true,
"maxDays": 7,
"minLevel": 0
},
"mailcowAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"meta": {
"appName": "PocketHost",
"appUrl": "https://pockethost-central.pockethost.io",
"confirmEmailChangeTemplate": {
"actionUrl": "{APP_URL}/_/#/users/confirm-email-change/{TOKEN}",
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
"subject": "Confirm your {APP_NAME} new email address"
},
"hideControls": false,
"resetPasswordTemplate": {
"actionUrl": "https://app.pockethost.io/login/password-reset/confirm?token={TOKEN}",
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
"subject": "Reset your {APP_NAME} password"
},
"senderAddress": "ben@pockethost.io",
"senderName": "Ben",
"verificationTemplate": {
"actionUrl": "https://app.pockethost.io/login/confirm-account?token={TOKEN}",
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
"subject": "Verify your {APP_NAME} email"
}
},
"microsoftAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"oidc2Auth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"oidc3Auth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"oidcAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"patreonAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"recordAuthToken": {
"duration": 1209600,
"secret": "7DgE4l39rrhFKF97V9yadQdolXGRHg0jSVA7j97CSHZ9QoK6Bn"
},
"recordEmailChangeToken": {
"duration": 1800,
"secret": "XbDSjPSPBISzRJbVj02mP2xHLpGrwegpAnqnUqrHbJWoMmhCid"
},
"recordFileToken": {
"duration": 120,
"secret": "rjZuZ3vofE30E7q4l3XjgADmwBo4gSCVSUS66aEB4gZBOs6kMV"
},
"recordPasswordResetToken": {
"duration": 1800,
"secret": "s3jkwjDqtboL9JCju73IPslQrflLngBIFO1JY2FH3Vf9oX4gkl"
},
"recordVerificationToken": {
"duration": 604800,
"secret": "8qNJXE3z90UwCzxafChqEPuNyeaEzAgSmvGKBPAOmM23yQzhv5"
},
"s3": {
"accessKey": "",
"bucket": "",
"enabled": false,
"endpoint": "",
"forcePathStyle": false,
"region": "",
"secret": ""
},
"smtp": {
"authMethod": "PLAIN",
"enabled": true,
"host": "host.docker.internal",
"localName": "",
"password": "",
"port": 1025,
"tls": false,
"username": ""
},
"spotifyAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"stravaAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"twitchAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"twitterAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"vkAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
},
"yandexAuth": {
"authUrl": "",
"clientId": "",
"clientSecret": "",
"displayName": "",
"enabled": false,
"pkce": null,
"tokenUrl": "",
"userApiUrl": ""
}
}