strapi / strapi

πŸš€ Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.
https://strapi.io
Other
64.14k stars 8.18k forks source link

lifecycles functions are not triggered on new user events #11828

Open creative-ae opened 3 years ago

creative-ae commented 3 years ago

Bug report

i need to run a code every-time a new user register. so i created: src/extensions/users-permissions/content-types/user/lifecycles.js However, the afterCreate function does NOT run upon user creation

Steps to reproduce the behavior

  1. createsrc/extensions/users-permissions/content-types/user/lifecycles.js
  2. add the following code in lifecycles.js
    module.exports = {
    async afterCreate(event) {
        const { result, params } = event;
        console.log('New user created');
    },
    }; 
  3. create / register new user

However, the afterCreate function does NOT run upon user creation

Expected behavior

afterCreate function in lifecycles get executed

System

fabio-nettis commented 2 years ago

Having the same issue. Creating a lifecycle hook for afterCreate does not get triggered.

zanio commented 2 years ago

I am currently having the same issues. Please any fix would be appreciated

inovosel commented 2 years ago

Strapi 4.0.5 has the same issue.

zanio commented 2 years ago

So i was having a similar issue and the fixed was simple, I changed the name of src/extensions/users-permissions/content-types/user/lifecycles.js to src/extensions/users-permissions/content-types/user/User.js and replace

module.exports = {
    async afterCreate(event) {
        const { result, params } = event;
        console.log('New user created');
    },
}; 

to

module.exports = {
lifecycles:{
    async afterCreate(event) {
        const { result, params } = event;
        console.log('New user created');
    },
  }
}; 

Take note of the lifecycles Object key. It must be present for this hooks to work

arielbk commented 2 years ago

You can register a subscriber at the database layer in your bootstrap function.

In your src/index.js:

module.exports = {
  register(/*{ strapi }*/) {},

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      // your lifecycle hooks
      async afterCreate() {
        console.log('user created!')
      },
    })
  },
}

This solution was provided by @kieranlangton on the Strapi forum after I had the same issue: https://forum.strapi.io/t/how-can-i-create-user-lifecycle-hooks-in-v4/13144

There are some caveats with the beforeX hooks, but the forum post also has a workaround for that.

creative-ae commented 2 years ago

So i was having a similar issue and the fixed was simple, I changed the name of src/extensions/users-permissions/content-types/user/lifecycles.js to src/extensions/users-permissions/content-types/user/User.js and replace

module.exports = {
    async afterCreate(event) {
        const { result, params } = event;
        console.log('New user created');
    },
}; 

to

module.exports = {
lifecycles:{
    async afterCreate(event) {
        const { result, params } = event;
        console.log('New user created');
    },
  }
}; 

Take note of the lifecycles Object key. It must be present for this hooks to work

that did not work

creative-ae commented 2 years ago

You can register a subscriber at the database layer in your bootstrap function.

In your src/index.js:

module.exports = {
  register(/*{ strapi }*/) {},

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      // your lifecycle hooks
      async afterCreate() {
        console.log('user created!')
      },
    })
  },
}

This solution was provided by @kieranlangton on the Strapi forum after I had the same issue: https://forum.strapi.io/t/how-can-i-create-user-lifecycle-hooks-in-v4/13144

There are some caveats with the beforeX hooks, but the forum post also has a workaround for that.

This is the only work around that worked out

cyansilver commented 2 years ago

hi,

I was wondering any new workaround for it? I tried everything but it still not work, even this one:

  register(/*{ strapi }*/) {},

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      // your lifecycle hooks
      async afterCreate() {
        console.log('user created!')
      },
    })
  },
}

Thank you

creative-ae commented 2 years ago

hi,

I was wondering any new workaround for it? I tried everything but it still not work, even this one:

  register(/*{ strapi }*/) {},

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      // your lifecycle hooks
      async afterCreate() {
        console.log('user created!')
      },
    })
  },
}

Thank you

it worked well for me. try double checking ur code

VTFLAB commented 2 years ago

You can register a subscriber at the database layer in your bootstrap function.

In your src/index.js:

module.exports = {
  register(/*{ strapi }*/) {},

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ['plugin::users-permissions.user'],

      // your lifecycle hooks
      async afterCreate() {
        console.log('user created!')
      },
    })
  },
}

This solution was provided by @kieranlangton on the Strapi forum after I had the same issue: https://forum.strapi.io/t/how-can-i-create-user-lifecycle-hooks-in-v4/13144

There are some caveats with the beforeX hooks, but the forum post also has a workaround for that.

I have confirmed the operation based on this excellent example. I was able to create an Activity for user registration by doing the following, just for reference

  bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ["plugin::users-permissions.user"],

      // your lifecycle hooks
      async afterCreate({ result }) {
        console.log("user created!");
        strapi.entityService.create("api::activity.activity", {
          data: {
            type: "UserRegister",
            action: "create",
            content: result,
          },
        });
      },
    });
  },

I think this issue has been resolved.

Strapi version: 4.3.6 plugin-users-permissions version: 4.3.6

vietlongn commented 2 years ago

Async hook in lifecycles.js is still not working on latest release (4.5.3) Any update on this? @derrickmehaffy (registering async hooks in bootstrap function is working as expected)

derrickmehaffy commented 1 year ago

I believe this is likely due to the compatibility layer we used in U&P for v4. We likely are skipping the normal db query engine :thinking:

nikita-fuchs commented 1 year ago

Can we please get the standard lifecycle hooks for users? This is literally the first thing I was trying when starting with strapi two days ago, and already hitting a wall. Please support this great functionality for the user data also.

Edit: I found a way - it's not perfect, but it suited my needs. hopefully it might help somebody else, too: https://forum.strapi.io/t/howto-finally-create-proper-lifecycle-hooks-for-users-permissions/24770/3

MokDevelopment commented 1 year ago

@nikita-fuchs Yeah. I totally agree... working with users feels like a nasty hack. I understand the idea behind the user feature being a plugin. But really this is so close to the base functionality of a cms, that it should be a core feature.

If it cannot be handled within the src folder - that most likely is expected by the most - it should at least be a hassle free process that is documented somewhere.

oliverkidd commented 1 year ago

Has there been any progress here?

I have successfully managed to link up events to user creation/update as shown below, but the lifecycle hook for a user being deleted just doesn't fire. I have tried every variation of the functions within the plugin (delete/destroy/remove) prefixing both before and after to all of them. no luck.

This code works beautifully for create/update. So why is delete/destroy not firing?

bootstrap({ strapi }) {
    strapi.db.lifecycles.subscribe({
      models: ["plugin::users-permissions.user"],
      // after user is registered
      async afterCreate(event) {
        // send email verification
        await strapi
          .service("api::email-verification.email-verification")
          .sendEmail(event.result.email, event.result.emailVerificationToken);
        // add contact to mailing list
        await strapi
          .service("api::sendinblue-link.sendinblue-link")
          .createContact(event.result);
      },
      // after user is updated
      async afterUpdate(event) {
        // update contact on mailing list
        await strapi
          .service("api::sendinblue-link.sendinblue-link")
          .updateContact(event.result);
      },
      // after user is deleted
      async afterDestroy(event) {
        // update contact on mailing list
        console.log("user deleted")
      },
    });
  },
dsfaccini commented 1 year ago

Is there any official solution? Why doesn't the normal lifecycles.ts method work?

When I use the index.ts, like the latest solution recommends, I get this ts error Argument of type '{ models: string[]; afterDestroy(event: any): Promise<void>; }' is not assignable to parameter of type 'Subscriber'. Object literal may only specify known properties, and 'models' does not exist in type 'Subscriber'.ts(2345)

Boegie19 commented 1 year ago

@dsfaccini The workaround strapi would suggest to use if they did would be strapi.db.lifecycles.subscribe

alexandrebodin commented 1 year ago

Hello, Here is how you can define the lifecycle hooks for the user contentType by using the plugin API to extend the users&permissions plugin

src/extensions/users-permissions/strapi-server.js

module.exports = (plugin) => {
  plugin.contentTypes.user.lifecycles = {
    async afterCreate(event) {
      const { result, params } = event;
      const ctx = strapi.requestContext.get();
      console.log('New user created', event, ctx);
    },
  };

  return plugin;
};

Let me share some context on why we do not simply allow dropping a file and why I would recommend using strapi.db.lifecycles instead:

This system needs to work for any and all plugins. As such it means some plugins might have contentTypes that already have lifecycles in place. So using the Plugin api to replace a plugin Lifecycles (like in the example) might just break the plugin. Strapi cannot guess what the user intention would be if a lifecycles.js file existed (overwrite/wrap, in what order...) So we leave it to the user to do the manual extension.

Therefore you have 2 options:

1 - Use the example if you want to replace a content type lifecycle with your own 2 - Use the strapi.db.lifecycles showed in some comments above to add new lifecycles without interfering with the existing ones.

Finally as showed in the example, If you want to access the ctx you can use the requestContext service to build more complex usecases.

I hope those details will help you choose the way you extend the lifecycles πŸ™

This example was done on strapi v4.11.4

⚠️ All That being said I'm keen on reintroducing the file for basic usecases: no overwrite just adding your own lifecycles on top of existing ones.

menty44 commented 1 year ago

If your using Strapi version 4+ with typescript make sure the lifecycle file has the extention .ts then put the code as follows.

export default { async afterCreate(event) { const {result, params} = event; console.log('New sample created', result); }, };

chestongo commented 11 months ago

If your using Strapi version 4+ with typescript make sure the lifecycle file has the extention .ts then put the code as follows,If your using Strapi version 4+ with typescript make sure the lifecycle file has the extention .ts then put the code as follows,

export default { async afterCreate(event) { const {result, params} = event; console.log('New sample created', result); }, };

Which type do you use for the event? I cannot find one with the result in it.

oshliaer commented 8 months ago

If your using Strapi version 4+ with typescript make sure the lifecycle file has the extention .ts then put the code as follows.

This doesn't work.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Time               β”‚ Sat Apr 06 2024 12:07:51 GMT+0300 (Moscow Stand… β”‚
β”‚ Launched in        β”‚ 10175 ms                                         β”‚
β”‚ Environment        β”‚ development                                      β”‚
β”‚ Process PID        β”‚ 473162                                           β”‚
β”‚ Version            β”‚ 4.21.1 (node v20.11.1)                           β”‚
β”‚ Edition            β”‚ Community                                        β”‚
β”‚ Database           β”‚ postgres                                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

This approach is fine

export default (plugin) => {
  plugin.contentTypes.user.lifecycles = {
    async afterCreate(event) {
      const { result, params } = event;
      const ctx = strapi.requestContext.get();
      console.log("New user created", event, ctx);
    },
  };
  return plugin;
};