diff --git a/.changeset/giant-knives-dance.md b/.changeset/giant-knives-dance.md
new file mode 100644
index 00000000..ea369839
--- /dev/null
+++ b/.changeset/giant-knives-dance.md
@@ -0,0 +1,5 @@
+---
+'@pockethost/plugin-maildev': patch
+---
+
+At launch, instances are configured to use maildev settings
diff --git a/packages/plugin-maildev/package.json b/packages/plugin-maildev/package.json
index 94c87fcf..7fb3ef1f 100644
--- a/packages/plugin-maildev/package.json
+++ b/packages/plugin-maildev/package.json
@@ -1,6 +1,6 @@
{
"name": "@pockethost/plugin-maildev",
- "version": "1.0.0",
+ "version": "0.0.0",
"repository": {
"type": "git",
"url": "http://github.com/pockethost/pockethost/packages/plugin-maildev"
@@ -23,6 +23,7 @@
"license": "MIT",
"dependencies": {
"commander": "^11.1.0",
+ "immer": "^10.1.1",
"maildev": "^2.1.0"
},
"peerDependencies": {
diff --git a/packages/plugin-maildev/src/constants.ts b/packages/plugin-maildev/src/constants.ts
index 8ef2064f..60ccef78 100644
--- a/packages/plugin-maildev/src/constants.ts
+++ b/packages/plugin-maildev/src/constants.ts
@@ -1,22 +1,30 @@
-import { join } from 'path'
+import { dirname, join } from 'path'
import { DEBUG } from 'pockethost'
import {
PH_HOME,
Settings,
logSettings,
+ mkBoolean,
mkNumber,
mkPath,
} from 'pockethost/core'
+import { fileURLToPath } from 'url'
export const PLUGIN_NAME = `plugin-maildev`
export const HOME_DIR =
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({
PH_MAILDEV_HOME: mkPath(HOME_DIR, { create: true }),
PH_MAILDEV_SMTP_PORT: mkNumber(1025),
PH_MAILDEV_WEB_ADMIN_PORT: mkNumber(1080),
+ PH_MAILDEV_ALWAYS_USE_LOCAL: mkBoolean(true),
})
export const PORT = () => settings.PH_MAILDEV_SMTP_PORT
diff --git a/packages/plugin-maildev/src/index.ts b/packages/plugin-maildev/src/index.ts
index 9e8c14fd..e7b78e63 100644
--- a/packages/plugin-maildev/src/index.ts
+++ b/packages/plugin-maildev/src/index.ts
@@ -1,13 +1,15 @@
import { Command } from 'commander'
+import { produce } from 'immer'
import MailDev from 'maildev'
import {
IS_DEV,
PocketHostPlugin,
onCliCommandsFilter,
+ onInstanceConfigFilter,
onServeAction,
onServeSlugsFilter,
} 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'
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) => {
return [...commands, MailDevCommand()]
})
diff --git a/packages/plugin-maildev/src/instance-app/hooks/plugin-maildev-smtp-settings.pb.js b/packages/plugin-maildev/src/instance-app/hooks/plugin-maildev-smtp-settings.pb.js
new file mode 100644
index 00000000..08420166
--- /dev/null
+++ b/packages/plugin-maildev/src/instance-app/hooks/plugin-maildev-smtp-settings.pb.js
@@ -0,0 +1,139 @@
+///
+
+$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}`)
+ }
+})
diff --git a/packages/plugin-maildev/src/instance-app/settings.json b/packages/plugin-maildev/src/instance-app/settings.json
new file mode 100644
index 00000000..7fe09481
--- /dev/null
+++ b/packages/plugin-maildev/src/instance-app/settings.json
@@ -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": "
Hello,
\nClick on the button below to confirm your new email address.
\n\n Confirm new email\n
\nIf you didn't ask to change your email address, you can ignore this email.
\n\n Thanks,
\n {APP_NAME} team\n
",
+ "subject": "Confirm your {APP_NAME} new email address"
+ },
+ "hideControls": false,
+ "resetPasswordTemplate": {
+ "actionUrl": "https://app.pockethost.io/login/password-reset/confirm?token={TOKEN}",
+ "body": "Hello,
\nClick on the button below to reset your password.
\n\n Reset password\n
\nIf you didn't ask to reset your password, you can ignore this email.
\n\n Thanks,
\n {APP_NAME} team\n
",
+ "subject": "Reset your {APP_NAME} password"
+ },
+ "senderAddress": "ben@pockethost.io",
+ "senderName": "Ben",
+ "verificationTemplate": {
+ "actionUrl": "https://app.pockethost.io/login/confirm-account?token={TOKEN}",
+ "body": "Hello,
\nThank you for joining us at {APP_NAME}.
\nClick on the button below to verify your email address.
\n\n Verify\n
\n\n Thanks,
\n {APP_NAME} team\n
",
+ "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": ""
+ }
+}