@paroicms/server
Version:
The ParoiCMS server
274 lines • 9.54 kB
JavaScript
import { makeSecret } from "@paroicms/internal-server-lib";
import { ApiError } from "@paroicms/public-server-lib";
import { type } from "arktype";
import { formatAccount, formatAuthenticatedAccount, } from "../../common/data-format.js";
import { simpleI18n } from "../../context.js";
import { getDevAccount, getPlatformAdminAccount, isDevAccountId, parsePlatformAdminAccountId, } from "../../helpers/special-account.helpers.js";
import { executeHook } from "../../plugin-services/make-backend-plugin-service.js";
const AccountRowAT = type({
id: "number",
email: "string",
name: "string|null",
preferences: "string|null",
passwordResetToken: "string|null",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
email: r.email,
name: r.name ?? undefined,
preferences: r.preferences ?? undefined,
passwordResetToken: r.passwordResetToken ?? undefined,
}));
const FindAccountByIdAndEmailRowAT = type({
id: "number",
email: "string",
name: "string|null",
preferences: "string|null",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
email: r.email,
name: r.name ?? undefined,
preferences: r.preferences ?? undefined,
}));
export async function findAccountByIdAndEmail(siteContext, payload) {
const account = await siteContext
.cn("PaAccount as a")
.select("a.id", "a.email", "a.name", "a.preferences")
.where({
"a.id": payload.id,
"a.email": payload.email,
})
.first();
if (!account)
throw new ApiError("Account not found", 404);
return FindAccountByIdAndEmailRowAT.assert(account);
}
const FindAccountByEmailRowAT = type({
id: "number",
email: "string",
name: "string|null",
preferences: "string|null",
passwordHash: "string|null",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
email: r.email,
name: r.name ?? undefined,
preferences: r.preferences ?? undefined,
passwordHash: r.passwordHash ?? undefined,
}));
export async function findAccountByEmail(siteContext, email) {
const account = await siteContext
.cn("PaAccount as a")
.select(["a.id", "a.email", "a.name", "a.preferences", "a.passwordHash"])
.where("a.email", email)
.first();
if (!account)
return;
return FindAccountByEmailRowAT.assert(account);
}
const GetAccountRowAT = type({
id: "number",
email: "string",
name: "string|null",
passwordResetToken: "string|null",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
email: r.email,
name: r.name ?? undefined,
passwordResetToken: r.passwordResetToken ?? undefined,
}));
export async function getAccount(siteContext, id) {
if (isDevAccountId(id))
return formatAccount(getDevAccount(id));
const parsedPlatformAccountId = parsePlatformAdminAccountId(id);
if (parsedPlatformAccountId) {
return formatAccount(getPlatformAdminAccount(parsedPlatformAccountId));
}
const found = await siteContext
.cn("PaAccount as a")
.select(["a.id", "a.email", "a.name", "a.passwordResetToken"])
.where("a.id", id)
.first();
if (!found)
throw new ApiError("Account not found", 404);
const account = GetAccountRowAT.assert(found);
return formatAccount(account);
}
export async function getAuthenticatedAccount(siteContext, id) {
if (isDevAccountId(id))
return formatAuthenticatedAccount(getDevAccount(id));
const parsedPlatformAccountId = parsePlatformAdminAccountId(id);
if (parsedPlatformAccountId) {
return formatAuthenticatedAccount(getPlatformAdminAccount(parsedPlatformAccountId));
}
const found = await siteContext
.cn("PaAccount as a")
.select(["a.id", "a.email", "a.name", "a.preferences", "a.passwordResetToken"])
.where("a.id", id)
.first();
if (!found)
throw new ApiError("Account not found", 404);
const account = AccountRowAT.assert(found);
return formatAuthenticatedAccount(account);
}
export async function getAllAccounts(siteContext) {
const accounts = await siteContext
.cn("PaAccount as a")
.select(["a.id", "a.email", "a.name", "a.preferences", "a.passwordResetToken"]);
const parsedAccounts = accounts
.map((account) => {
return AccountRowAT.assert(account);
})
.map((user) => formatAccount(user));
return parsedAccounts;
}
export async function setAccountPreferences(siteContext, accountId, values) {
await siteContext
.cn("PaAccount")
.where({ id: accountId })
.update({ preferences: JSON.stringify(values) });
}
const CreateAccountInsertedAT = type({
id: "number",
"+": "reject",
}).pipe((r) => ({
id: String(r.id),
}));
export async function createAccount(siteContext, payload) {
const account = await findAccountByEmail(siteContext, payload.email);
if (account)
throw new ApiError("email already exists", 409);
const [inserted] = await siteContext
.cn("PaAccount")
.insert({
email: payload.email,
name: payload.name,
preferences: JSON.stringify({ language: payload.language }),
})
.returning("id");
const id = CreateAccountInsertedAT.assert(inserted).id;
const newAccount = await findAccountById(siteContext, id);
if (!newAccount) {
throw new ApiError("Failed to get last insert account", 500);
}
if (payload.accountType === "google") {
return await createGoogleAccount(siteContext, {
payload,
newAccount,
});
}
await resetAccountToken(siteContext, {
id: newAccount.id,
email: newAccount.email,
language: payload.language,
});
return formatAccount(newAccount);
}
async function createGoogleAccount(siteContext, { payload, newAccount, }) {
const subject = simpleI18n.translate({
key: "accountCreation.subject",
language: payload.language,
});
const message = simpleI18n.translate({
key: "accountCreation.message",
language: payload.language,
args: [`${siteContext.siteUrl}/adm`],
});
await executeHook(siteContext, "sendMail", {
value: {
subject: `${subject} - ${siteContext.fqdn}`,
html: message,
to: newAccount.email,
},
});
return formatAccount(newAccount);
}
export async function updateAccount(siteContext, accountId, payload) {
const account = await findAccountById(siteContext, accountId);
if (!account)
throw new ApiError("Account not found", 404);
await siteContext.cn("PaAccount").where({ id: account.id }).update(payload);
return formatAccount({
...account,
email: payload.email ?? account.email,
name: payload.name ?? account.name,
});
}
export async function deleteAccount(siteContext, accountId) {
const account = await findAccountById(siteContext, accountId);
if (!account)
throw new ApiError("Account not found", 404);
await siteContext.cn("PaAccount").where({ id: account.id }).delete();
}
export async function resetAccountPassword(siteContext, accountId) {
const account = await findAccountById(siteContext, accountId);
if (!account)
throw new ApiError("Account not found", 404);
const language = getAccountLanguage(siteContext, account);
await resetAccountToken(siteContext, { id: account.id, email: account.email, language });
}
const FindAccountByIdRowAT = type({
id: "number",
email: "string",
name: "string|null",
preferences: "string|null",
passwordHash: "string|null",
passwordResetToken: "string|null",
"+": "reject",
}).pipe((data) => ({
id: String(data.id),
email: data.email,
name: data.name ?? undefined,
preferences: data.preferences ?? undefined,
passwordHash: data.passwordHash ?? undefined,
passwordResetToken: data.passwordResetToken ?? undefined,
}));
async function findAccountById(siteContext, id) {
const found = await siteContext
.cn("PaAccount as a")
.select([
"a.id",
"a.email",
"a.name",
"a.preferences",
"a.passwordHash",
"a.passwordResetToken",
])
.where("a.id", id)
.first();
if (!found)
return;
return FindAccountByIdRowAT.assert(found);
}
async function resetAccountToken(siteContext, account) {
const passwordResetToken = makeSecret(60);
await siteContext.cn("PaAccount").where({ id: account.id }).update({
passwordResetToken,
passwordHash: null,
});
const subject = simpleI18n.translate({
key: "accountPasswordReset.subject",
language: account.language,
});
const resetUrl = `${siteContext.siteUrl}/adm/?account=${account.id}&reset-password=${encodeURIComponent(passwordResetToken)}`;
const message = simpleI18n.translate({
key: "accountPasswordReset.message",
language: account.language,
args: [resetUrl],
});
await executeHook(siteContext, "sendMail", {
value: {
subject: `${subject} - ${siteContext.fqdn}`,
html: message,
to: account.email,
},
});
}
function getAccountLanguage(siteContext, account) {
const preferences = account.preferences ? JSON.parse(account.preferences) : undefined;
return preferences?.language ?? siteContext.siteSchema.defaultLanguage;
}
//# sourceMappingURL=account.queries.js.map