mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-26 23:46:28 -08:00
Compare commits
2 commits
40e7350390
...
5a030684db
Author | SHA1 | Date | |
---|---|---|---|
5a030684db | |||
May | ae4252e898 |
15
src/reset.html
Normal file
15
src/reset.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>reset</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>monofile password reset</h1>
|
||||||
|
<form>
|
||||||
|
<input name="password" type="password">
|
||||||
|
READY... STEADY... <input type="submit" name='FYLE!!!'>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -94,9 +94,11 @@ const validators: {
|
||||||
requireProofOfIdentity: true,
|
requireProofOfIdentity: true,
|
||||||
validator: (actor, target, params, ctx) => {
|
validator: (actor, target, params, ctx) => {
|
||||||
|
|
||||||
|
const email = params.email.toLowerCase()
|
||||||
|
|
||||||
if (!Configuration.mail.enabled) return [501, "email not enabled on instance"]
|
if (!Configuration.mail.enabled) return [501, "email not enabled on instance"]
|
||||||
|
|
||||||
if (!params.email) {
|
if (!email) {
|
||||||
if (target.email) {
|
if (target.email) {
|
||||||
sendMail(
|
sendMail(
|
||||||
target.email,
|
target.email,
|
||||||
|
@ -107,11 +109,11 @@ const validators: {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor.admin) return params.email || undefined
|
if (actor.admin) return email || undefined
|
||||||
|
|
||||||
// send verification email
|
// send verification email
|
||||||
|
|
||||||
const tryCode = CodeMgr.code("verifyEmail", target.id, params.email)
|
const tryCode = CodeMgr.code("verifyEmail", target.id, email)
|
||||||
|
|
||||||
if (!tryCode.success)
|
if (!tryCode.success)
|
||||||
return [429, tryCode.error]
|
return [429, tryCode.error]
|
||||||
|
@ -119,12 +121,12 @@ const validators: {
|
||||||
const { code } = tryCode
|
const { code } = tryCode
|
||||||
|
|
||||||
sendMail(
|
sendMail(
|
||||||
params.email,
|
email,
|
||||||
`Hey there, ${target.username} - let's connect your email`,
|
`Hey there, ${target.username} - let's connect your email`,
|
||||||
`<b>Hello there!</b> You are recieving this message because you decided to link your email, <span code>${
|
`<b>Hello there!</b> You are recieving this message because you decided to link your email, <span code>${
|
||||||
params.email.split("@")[0]
|
email.split("@")[0]
|
||||||
}<span style="opacity:0.5">@${
|
}<span style="opacity:0.5">@${
|
||||||
params.email.split("@")[1]
|
email.split("@")[1]
|
||||||
}</span></span>, to your account, <span username>${
|
}</span></span>, to your account, <span username>${
|
||||||
target.username
|
target.username
|
||||||
}</span>. If you would like to continue, please <a href="https://${ctx.req.header(
|
}</span>. If you would like to continue, please <a href="https://${ctx.req.header(
|
||||||
|
@ -258,6 +260,10 @@ const UserUpdateScheme = z.union([
|
||||||
}).strict()
|
}).strict()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const PasswordResetScheme = z.object({
|
||||||
|
email: AccountSchemas.Account.shape.email
|
||||||
|
}).required()
|
||||||
|
|
||||||
export default function (files: Files) {
|
export default function (files: Files) {
|
||||||
router.post("/", scheme(z.object({
|
router.post("/", scheme(z.object({
|
||||||
username: AccountSchemas.Username,
|
username: AccountSchemas.Username,
|
||||||
|
@ -392,5 +398,35 @@ export default function (files: Files) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/:user/requestPasswordReset",
|
||||||
|
noAPIAccess,
|
||||||
|
scheme(PasswordResetScheme),
|
||||||
|
async (ctx) => {
|
||||||
|
const { email } = ctx.get("parsedScheme") as z.infer<typeof PasswordResetScheme>
|
||||||
|
const actor = ctx.get("account")
|
||||||
|
const target = ctx.get("target")
|
||||||
|
|
||||||
|
if (actor)
|
||||||
|
return ServeError(ctx, 404, "account not found")
|
||||||
|
if (!target.email || email !== target.email)
|
||||||
|
return ServeError(ctx, 401, "bad email")
|
||||||
|
|
||||||
|
const tryCode = CodeMgr.code("recoverAccount", target.id, null, 15*60*1000)
|
||||||
|
|
||||||
|
if (!tryCode.success)
|
||||||
|
return ServeError(ctx, 429, tryCode.error)
|
||||||
|
|
||||||
|
await sendMail(
|
||||||
|
target.email!,
|
||||||
|
"Password reset requested",
|
||||||
|
`A password reset for your account, <span username>${target.username}</span>, has been requested.`
|
||||||
|
+ `Go to https://${ctx.req.header("Host")}/go/verify/${tryCode.code.id} to reset your password.`
|
||||||
|
)
|
||||||
|
|
||||||
|
return ctx.text("email sent")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type Files from "../../../lib/files.js"
|
||||||
import * as CodeMgr from "../../../lib/codes.js"
|
import * as CodeMgr from "../../../lib/codes.js"
|
||||||
import { Hono } from "hono"
|
import { Hono } from "hono"
|
||||||
import { getAccount, login } from "../../../lib/middleware.js"
|
import { getAccount, login } from "../../../lib/middleware.js"
|
||||||
|
import { sendMail } from "../../../lib/mail.js"
|
||||||
export let router = new Hono<{
|
export let router = new Hono<{
|
||||||
Variables: {
|
Variables: {
|
||||||
account: Accounts.Account
|
account: Accounts.Account
|
||||||
|
@ -36,5 +37,43 @@ export default function (files: Files) {
|
||||||
} else return ServeError(ctx, 404, "code not found")
|
} else return ServeError(ctx, 404, "code not found")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.get("/reset/:code", getAccount, async (ctx) => {
|
||||||
|
let currentAccount = ctx.get("account")
|
||||||
|
let code = CodeMgr.codes.verifyEmail.byId.get(ctx.req.param("code"))
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
if (currentAccount != undefined && !code.check(currentAccount.id)) {
|
||||||
|
return ServeError(ctx, 403, "you are logged in on a different account")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentAccount) {
|
||||||
|
let ac = Accounts.getFromId(code.for)
|
||||||
|
if (ac) currentAccount = ac
|
||||||
|
else return ServeError(ctx, 401, "could not locate account")
|
||||||
|
}
|
||||||
|
|
||||||
|
let pwd = ctx.req.query("password")
|
||||||
|
|
||||||
|
if (!pwd) {
|
||||||
|
return ctx.html(
|
||||||
|
await fs.readFile(process.cwd() + "/dist/reset.html", "utf-8")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (currentAccount.email) {
|
||||||
|
sendMail(
|
||||||
|
currentAccount.email,
|
||||||
|
`Your login details have been updated`,
|
||||||
|
`<b>Hello there!</b> Your password on your account, <span username>${currentAccount.username}</span>, has been successfully reset. ` +
|
||||||
|
`Please update your saved login details accordingly.`
|
||||||
|
).catch()
|
||||||
|
}
|
||||||
|
|
||||||
|
Accounts.password.set(currentAccount.id, pwd)
|
||||||
|
login(ctx, code.for)
|
||||||
|
return ctx.redirect("/")
|
||||||
|
}
|
||||||
|
} else return ServeError(ctx, 404, "code not found")
|
||||||
|
})
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
|
@ -12,6 +12,7 @@ export default defineConfig({
|
||||||
main: resolve(__dirname, "src/index.html"),
|
main: resolve(__dirname, "src/index.html"),
|
||||||
download: resolve(__dirname, "src/download.html"),
|
download: resolve(__dirname, "src/download.html"),
|
||||||
error: resolve(__dirname, "src/error.html"),
|
error: resolve(__dirname, "src/error.html"),
|
||||||
|
reset: resolve(__dirname, "src/reset.html"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue