Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P7)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P7)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P7): Implementation of Front-end User Authentication Function Based on Muse UI 3.0

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P7): Implementation of Front-end User Authentication Function Based on Muse UI 3.0

Introduce Material Design font and icon files

Since our chat room project front-end interface is based on the Muse UI, while Muse UI based on Material Design realization, so before you start, you need to import documents in view resources/views/index.blade.php of <head> the </head> following new font and icon files introduced between the label:

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="//at.alicdn.com/t/font_500440_9oye91czwt8.css">

This is the font and icon library recommended by Muse UI(the last one is the Alibaba icon icon style file, which will be used in the chat room window). After the introduction, refresh the home page and you can see the following icons are rendered:

This is image title

Note: Muse UI is an elegant Material Design UI component library based on Vue 2.0. It can be likened to Bootstrap, iView, Element and other UI frameworks.

Adjust login and registration interface code to Muse UI 3.0

Click on the "我的" link, prompting you to log in:

This is image title

Click the "去登录" link to enter the login page to complete user authentication:

This is image title

At first glance, it may seem that there is no username and password input box, but it is actually the reason for the color overlay of the background image:

This is image title

But this style and Vue-Webchat renderings slightly different, because we are now installing the 3.0 version of the Muse UI, while the Vue-Webchat using Muse UI version 2.0, it is necessary to resources/js/pages/Login.vue templates do for Muse UI component code Some adjustments:

<div class="content">
  <form action="" name="form2">
    <mu-text-field v-model="username" label="User" label-float icon="account_circle" full-width></mu-text-field>
    <br/>
    <mu-text-field v-model="password" type="password" label="password" label-float icon="locked" full-width></mu-text-field>
    <br/>
    <div class="btn-radius" @click="submit">Sign in</div>
  </form>
  <div @click="register" class="tip-user">Register Account</div>
</div>

...

export default {
  data() {
    return {
        loading: "",
        username: "",
        password: ""
    };
  },
  methods: {
    async submit() {
      const name = this.username.trim();
      const password = this.password.trim();
      ...

Rerun the npm run dev compiler front-end resources, and then force a refresh the login page, you can see the following renderings, and still OK:

This is image title

Incidentally, the registration component resources/js/pages/Register.vue template code to be modified, the logic and Login.vue the same:

<div class="content">
  <form action="" name="form1">
    <mu-text-field v-model="username" label="Username" label-float icon="account_circle" full-width></mu-text-field>
    <br/>
    <mu-text-field v-model="password" type="password" label="Password" label-float icon="locked" full-width></mu-text-field>
    <br/>
    <div class="btn-radius" @click="submit">Signup</div>
  </form>
  <div @click="login" class="tip-user">
    I already have a account
  </div>
</div>

...

export default {
  data() {
      return {
          username: "",
          password: ""
      };
  },
  methods: {
    async submit() {
      const name = this.username.trim();
      const password = this.password.trim();
      ...

Then run npm run dev recompiled front-end resources.

Joint debugging of front-end and back-end registration interfaces

Front-end registration form submission logic

Next, we start with user registration, and jointly adjust the front-end and back-end user authentication related interfaces to open the user registration interface http://webchats.test/#/register:

This is image title

Commit logic source code by reading the front-end registration form, you can see the logic and back-end is ultimately responsible for registration interface interaction is located resources/js/api/server.js in:

// Registration interface
RegisterUser: data => Axios.post('/register', data),

Referring to our previous backend API functions to achieve certification tutorials route definition, where the backend interface will need to adjust to /api/register, as we are resources/js/api/axios.js already defined baseURL, so here it does not make any adjustments:

const baseURL = '/api';

...

// Request unified processing
instance.interceptors.request.use(async config => {
    if (config.url && config.url.charAt(0) === '/') {
        config.url = `${baseURL}${config.url}`;
    }

    return config;
}, error => Promise.reject(error));

In addition, the front end to the rear end of the data transfer register form further comprises a default avatar URL (corresponding to logically located Register.vue in):

const data = {
    name: name,
    password: password,
    src: src
};
const res = await this.$store.dispatch("registerSubmit", data);

This field can be mapped to the rear end of users the new table avatar fields, in order to simplify the front-end logic, we can assume that the user input is a mailbox account and mailbox rear prefix taken as the user name.

Backend authentication interface adjustment

Corresponding to the app/Http/Controllers/AuthController.php modified rear end user registration process register to achieve the following:

public function register(Request $request)
{
    // Verify registration fields
    $validator = Validator::make($request->all(), [
        'name' => 'bail|required|email|max:100|unique:users',
        'password' => 'bail|required|string|min:6',
        'src' => 'bail|active_url|max:255'
    ]);
    if ($validator->fails()) {
        return [
            'errno' => 1,
            'data' => $validator->errors()->first()
        ];
    }

    // Create user in database and return
    $email = $request->input('name');
    try {
        $user = User::create([
            'name' => substr($email, 0, strpos($email, '@')),
            'email' => $email,
            'avatar' => $request->input('src'),
            'password' => Hash::make($request->input('password')),
            'api_token' => Str::random(60)
        ]);
        if ($user) {
            return [
                'errno' => 0,
                'data' => $user
            ];
        } else {
            return [
                'errno' => 1,
                'data' => 'Failed to save user to database'
            ];
        }
    } catch (QueryException $exception) {
        return [
            'errno' => 1,
            'data' => 'Save user to database exception:' . $exception->getMessage()
        ];
    }
}

We modified the form validation rules and return data format to fit the front-end code. Also, do not forget to modify the App\User model class of $fillable property, the new avatar field:

protected $fillable = [
    'name', 'email', 'password', 'api_token', 'avatar'
];

Restart the Swoole HTTP server for the back-end interface adjustment to take effect:

bin/laravels reload

Modify user interface code to fit Muse UI 3.0

Vue-Webchat this demonstration project is relatively simple piece of processing in the user authentication, user authentication is successful will save user information localStorage, as long as localStorage contained in userid it that the user has been authenticated, userid is the user account, the corresponding logic is saved after user registration or login Finished:

const res = await this.$store.dispatch("registerSubmit", data);
if (res.status === "success") {
    Toast({
        content: res.data.data,
        timeout: 1000,
        background: "#2196f3"
    });
    this.$store.commit("setUserInfo", {
        type: "userid",
        value: data.name
    });
    this.$store.commit("setUserInfo", {
        type: "src",
        value: data.src
    });
    this.getSvgModal.$root.$options.clear();
    this.$store.commit("setSvgModal", null);
    this.$router.push({ path: "/" });
    socket.emit("login", { name });
} else {
    await Alert({
        content: res.data.data
    });
}

But now in this "我的" page error, suggesting that <mu-raised-button> this component registration fails, this is Muse UI 3.0 version of the reason, the new version has been incorporated into this component <mu-button>. So we need to open a user interface where the file resources/js/pages/Home.vue, modify the corresponding component templates code show as below:

<div class="logout">
  <mu-button @click="logout" class="demo-raised-button" full-width>Sign out</mu-button>
</div>

Further, Muse UI 3.0 pair of <mu-list>use has been adjusted, it is necessary to adjust the reference code corresponding to the following components:

<mu-list>
    <mu-list-item button @click="changeAvatar">
      <mu-list-item-action>
        <mu-icon slot="left" value="send"/>
      </mu-list-item-action>
      <mu-list-item-title>Modify avatar</mu-list-item-title>
    </mu-list-item>
    <mu-list-item button @click="handleTips">
      <mu-list-item-action>
        <mu-icon slot="left" value="inbox"/>
      </mu-list-item-action>
      <mu-list-item-title>Sponsor</mu-list-item-title>
    </mu-list-item>
    <mu-list-item button @click="handleGithub">
      <mu-list-item-action>
        <mu-icon slot="left" value="grade"/>
      </mu-list-item-action>
      <mu-list-item-title>Github address</mu-list-item-title>
    </mu-list-item>
    <mu-list-item button @click="rmLocalData">
      <mu-list-item-action>
        <mu-icon slot="left" value="drafts"/>
      </mu-list-item-action>
      <mu-list-item-title>Clear cache</mu-list-item-title>
    </mu-list-item>
  </mu-list>

Then run npm run dev recompiled front-end resources, then refresh "我的" page:

This is image title

You can see the user center interface that is normally rendered, and the list items also support clicking:

This is image title

We click the "Logout" button on this page to log out, and then click the "我的" link at the bottom of the page again to enter the login page, and test the call to the login interface.

Joint debugging of front-end and back-end login interfaces

Backend login interface adjustment

And registration function as front-end login screen and function need not be adjusted, and registered the corresponding logic is the same, ultimately through /api/login the login logic interface call back-end user. Backend login interface needs to be adjusted to fit the front end of the login form submission, in app/Http/Controllers/AuthController.php, modify login as follows:

public function login(Request $request)
{
    // Verify login fields
    $validator = Validator::make($request->all(), [
        'name' => 'required|email|string',
        'password' => 'required|string',
    ]);
    if ($validator->fails()) {
        return [
            'errno' => 1,
            'data' => $validator->errors()->first()
        ];
    }

    $email = $request->input('name');
    $password = $request->input('password');
    $user = User::where('email', $email)->first();
    // Token information is returned if the user successfully checks
    if ($user && Hash::check($password, $user->password)) {
        $user->api_token = Str::random(60);
        $user->save();
        return [
            'errno' => 0,
            'data' => $user
        ];
    }

    return [
        'errno' => 1,
        'data' => 'Username and password do not match, please re-enter'
    ];
}

Restart the Swoole HTTP server for the backend modification to take effect:

bin/laravels reload

Test user login function

Then fill in the user information on the front-end login page and click the "Login" button:

Login

After login successfully, it also jumps to the homepage. This time, we clicked the "我的" link at the bottom of the page, but it showed that the authentication was not successful. This is because the account information, the backend interface field and the frontend default interface field are obtained from the returned data after the backend login interface is successfully called. res.data.data.email Does not match, the back-end user account field is , and the front-end access is a non-existent field res.data.name. In addition, the avatar field does not match:

const res = await this.$store.dispatch("loginSubmit", data);
if (res.status === "success") {
    Toast({
        content: res.data.data,
        timeout: 1000,
        background: "#2196f3"
    });
    this.$store.commit("setUserInfo", {
        type: "userid",
        value: res.data.name
    });
    this.$store.commit("setUserInfo", {
        type: "src",
        value: res.data.src
    });
    this.getSvgModal.$root.$options.clear();
    this.$store.commit("setSvgModal", null);
    this.$router.push({ path: "/" });
    socket.emit("login", { name });
} else {
    Alert({
        content: res.data.data
    });
}

Adjust the logic of saving user information after the front-end user logs in successfully

Through this opportunity, we register and login to the back-end interface calls successfully unified gets saved data from back-end to front-end users localStorage, while preserving api_tokenthe field to the front end for future use.

In the first resources/js/pages/Login.vue, modify the back-end interface call log on user information stored after the success localStorage of the relevant code:

const res = await this.$store.dispatch("loginSubmit", data);
if (res.status === "success") {
    Toast({
        content: res.data.message,
        timeout: 1000,
        background: "#2196f3"
    });
    this.$store.commit("setUserInfo", {
        type: "userid",
        value: res.data.user.email
    });
    this.$store.commit("setUserInfo", {
        type: "token",
        value: res.data.user.api_token
    });
    this.$store.commit("setUserInfo", {
        type: "src",
        value: res.data.user.avatar
    });
    this.getSvgModal.$root.$options.clear();
    this.$store.commit("setSvgModal", null);
    this.$router.push({ path: "/" });
    socket.emit("login", { name });
} else {
    Alert({
        content: res.data.message
    });
}

At the same time, the back-end interface also separates user data from message data:

public function register(Request $request)
{
    // Verify registration fields
    $validator = Validator::make($request->all(), [
        'name' => 'bail|required|email|max:100|unique:users',
        'password' => 'bail|required|string|min:6',
        'src' => 'bail|active_url|max:255'
    ]);
    if ($validator->fails()) {
        return [
            'errno' => 1,
            'message' => $validator->errors()->first()
        ];
    }

    // Create user in database and return
    $email = $request->input('name');
    try {
        $user = User::create([
            'name' => substr($email, 0, strpos($email, '@')),
            'email' => $email,
            'avatar' => $request->input('src'),
            'password' => Hash::make($request->input('password')),
            'api_token' => Str::random(60)
        ]);
        if ($user) {
            return [
                'errno' => 0,
                'user' => $user,
                'message' => 'User registered successfully'
            ];
        } else {
            return [
                'errno' => 1,
                'message' => 'Failed to save user to database'
            ];
        }
    } catch (QueryException $exception) {
        return [
            'errno' => 1,
            'message' => 'Save user to database exception:' . $exception->getMessage()
        ];
    }
}

public function login(Request $request)
{
    // Verify login fields
    $validator = Validator::make($request->all(), [
        'name' => 'required|email|string',
        'password' => 'required|string',
    ]);
    if ($validator->fails()) {
        return [
            'errno' => 1,
            'message' => $validator->errors()->first()
        ];
    }

    $email = $request->input('name');
    $password = $request->input('password');
    $user = User::where('email', $email)->first();
    // Token information is returned if the user successfully checks
    if ($user && Hash::check($password, $user->password)) {
        $user->api_token = Str::random(60);
        $user->save();
        return [
            'errno' => 0,
            'user' => $user,
            'message' => 'User login successful'
        ];
    }

    return [
        'errno' => 1,
        'message' => 'Username and password do not match, please re-enter'
    ];
}

public function logout(Request $request)
{
    $user = Auth::guard('auth:api')->user();
    if (!$user) {
        return [
            'errno' => 1,
            'message' => 'User has logged out'
        ];
    }
    $userModel = User::find($user->id);
    $userModel->api_token = null;
    $userModel->save();
    return [
        'errno' => 0,
        'message' => 'User exited successfully'
    ];
}

Modify the logic of saving user information after the front-end user registration is successful

Review resources/js/pages/Register.vue Related localStorage code is as follows:

const res = await this.$store.dispatch("registerSubmit", data);
if (res.status === "success") {
    Toast({
        content: res.data.message,
        timeout: 1000,
        background: "#2196f3"
    });
    this.$store.commit("setUserInfo", {
        type: "userid",
        value: res.data.user.email
    });
    this.$store.commit("setUserInfo", {
        type: "token",
        value: res.data.user.api_token
    });
    this.$store.commit("setUserInfo", {
        type: "src",
        value: res.data.user.avatar
    });
    this.getSvgModal.$root.$options.clear();
    this.$store.commit("setSvgModal", null);
    this.$router.push({ path: "/" });
    socket.emit("login", { name });
} else {
      await Alert({
        content: res.data.message
      });
}

Modify front-end user logout and clear user information logic

The user authentication logic related to WebSocket will be discussed in the next tutorial. Finally resources/js/pages/Home.vue cleared when the user exits the token data:

async logout() {
    const data = await Confirm({
        title: "prompt",
        content: "Do you have the heart to leave?"
    });
    if (data === "submit") {
        clear();
        this.$store.commit("setUserInfo", {
            type: "userid",
            value: ""
        });
        this.$store.commit("setUserInfo", {
            type: "token",
            value: ""
        });
        this.$store.commit("setUserInfo", {
            type: "src",
            value: ""
        });
        this.$store.commit("setUnread", {
            room1: 0,
            room2: 0
        });
        this.$router.push("/");
        this.$store.commit("setTab", false);
    }
},

Recompile the front-end resources:

npm run dev

Restart the Swoole HTTP server:

bin/laravels reload

Test user login again

Access the login page again, after a successful login, the top of the page will be prompted to log in successfully, go to "我的" page, you can also see the login has been successful, and localStorage you can see in the new token field:

Test user login

At this point, the front-end and back-end linkage user authentication function has been completed. Next, we will formally begin to enter the code development of real-time chat rooms based on WebSocket.

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P6)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P6): Establish a Connection Between the Socket.io Client and The Swoole Websocket server

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14): Send a Picture Message

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P4): Front-end Resource Initialization