feat(medusa,medusa-cli): Added an invite option to the create user command, and allow seeding publishable api keys (#4192)

* feat(medusa,medusa-cli): Added an invite option to the create user command

* ensure process exits for invites

* addressed PR comments

* allow seeding publishable api keys
This commit is contained in:
Shahed Nasser
2023-06-07 20:34:27 +02:00
committed by GitHub
parent 854022daa8
commit 8676ee7a2e
5 changed files with 80 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@medusajs/medusa-cli": patch
"@medusajs/medusa": patch
---
feat(medusa,medusa-cli): Added an invite option to the create user command, and allow seeding publishable api keys
+5
View File
@@ -296,6 +296,11 @@ function buildLocalCommands(cli, isLocalProject) {
alias: `id`, alias: `id`,
type: `string`, type: `string`,
describe: `User's id.`, describe: `User's id.`,
})
.option(`invite`, {
type: `boolean`,
describe: `If flag is set, an invitation will be created instead of a new user and the invite token will be returned.`,
default: false,
}), }),
handler: handlerP( handler: handlerP(
getCommandHandler(`user`, (args, cmd) => { getCommandHandler(`user`, (args, cmd) => {
+35
View File
@@ -17,6 +17,7 @@ import {
ProductService, ProductService,
ProductVariantService, ProductVariantService,
RegionService, RegionService,
SalesChannelService,
ShippingOptionService, ShippingOptionService,
ShippingProfileService, ShippingProfileService,
StoreService, StoreService,
@@ -26,6 +27,8 @@ import { ConfigModule } from "../types/global"
import { CreateProductInput } from "../types/product" import { CreateProductInput } from "../types/product"
import { CreateProductCategoryInput } from "../types/product-category" import { CreateProductCategoryInput } from "../types/product-category"
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations" import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
import PublishableApiKeyService from "../services/publishable-api-key"
import { SalesChannel } from "../models"
type SeedOptions = { type SeedOptions = {
directory: string directory: string
@@ -101,6 +104,12 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
const productCategoryService: ProductCategoryService = container.resolve( const productCategoryService: ProductCategoryService = container.resolve(
"productCategoryService" "productCategoryService"
) )
const publishableApiKeyService: PublishableApiKeyService = container.resolve(
"publishableApiKeyService"
)
const salesChannelService: SalesChannelService = container.resolve(
"salesChannelService"
)
/* eslint-disable */ /* eslint-disable */
const productVariantService: ProductVariantService = container.resolve( const productVariantService: ProductVariantService = container.resolve(
@@ -122,6 +131,7 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
categories = [], categories = [],
shipping_options, shipping_options,
users, users,
publishable_api_keys = [],
} = JSON.parse(fs.readFileSync(resolvedPath, `utf-8`)) } = JSON.parse(fs.readFileSync(resolvedPath, `utf-8`))
const gcProfile = await shippingProfileService.retrieveGiftCardDefault() const gcProfile = await shippingProfileService.retrieveGiftCardDefault()
@@ -235,6 +245,31 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
} }
} }
} }
let defaultSalesChannel: SalesChannel | null = null
try {
defaultSalesChannel = await salesChannelService
.withTransaction(tx)
.retrieveDefault()
} catch (e) {
defaultSalesChannel = null
}
for (const pak of publishable_api_keys) {
const publishableApiKey = await publishableApiKeyService
.withTransaction(tx)
.create(pak, {
loggedInUserId: "",
})
// attach to default sales channel if exists
if (defaultSalesChannel) {
await publishableApiKeyService.addSalesChannels(publishableApiKey.id, [
defaultSalesChannel.id,
])
}
}
}) })
track("CLI_SEED_COMPLETED") track("CLI_SEED_COMPLETED")
+22 -3
View File
@@ -5,8 +5,16 @@ import express from "express"
import { track } from "medusa-telemetry" import { track } from "medusa-telemetry"
import loaders from "../loaders" import loaders from "../loaders"
import Logger from "../loaders/logger"
export default async function ({ directory, id, email, password, keepAlive }) { export default async function ({
directory,
id,
email,
password,
keepAlive,
invite,
}) {
track("CLI_USER", { with_id: !!id }) track("CLI_USER", { with_id: !!id })
const app = express() const app = express()
try { try {
@@ -15,8 +23,19 @@ export default async function ({ directory, id, email, password, keepAlive }) {
expressApp: app, expressApp: app,
}) })
const userService = container.resolve("userService") if (invite) {
await userService.create({ id, email }, password) const inviteService = container.resolve("inviteService")
await inviteService.create(email, "admin")
const invite = await inviteService.list({
user_email: email,
})
Logger.info(`
Invite token: ${invite[0].token}
Open the invite in Medusa Admin at: [your-admin-url]/invite?token=${invite[0].token}`)
} else {
const userService = container.resolve("userService")
await userService.create({ id, email }, password)
}
} catch (err) { } catch (err) {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
+12
View File
@@ -177,6 +177,18 @@ class UserService extends TransactionBaseService {
} }
const validatedEmail = validateEmail(user.email) const validatedEmail = validateEmail(user.email)
const userEntity = await userRepo.findOne({
where: { email: validatedEmail },
})
if (userEntity) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"A user with the same email already exists."
)
}
if (password) { if (password) {
const hashedPassword = await this.hashPassword_(password) const hashedPassword = await this.hashPassword_(password)
createData.password_hash = hashedPassword createData.password_hash = hashedPassword