当前位置: 首页 > 工具软件 > CSS-Only Chat > 使用案例 >

Feathers框架 2:Guides --> A Chat Application

甄华清
2023-12-01

2.1 Creating the application

$ npm install @feathersjs/cli -g

$ mkdir feathers-chat

$ feathers generate app   # 创建APP

? Project name feathers-chat
? Description A Feathers chat application
? What folder should the source files live in? src
? Which package manager are you using (has to be installed globally)? npm
? What type of API are you making? REST, Realtime via Socket.io
   create package.json
   create config/default.json
   create LICENSE
   create public/favicon.ico
   create public/index.html
   create .editorconfig
   create .eslintrc.json
   create src/app.hooks.js
   create src/channels.js
   create src/hooks/logger.js
   create src/index.js
   create src/middleware/index.js
   create src/services/index.js
   create .gitignore
   create README.md
   create src/app.js
   create test/app.test.js
   create config/production.json
$ npm start

$ npm test

2.2 Creating a service

Generating a service :  

$ feathers generate service

? What kind of service is it? NeDB
? What is the name of the service? messages
? Which path should the service be registered on? /messages
? What is the database connection string? nedb://../data
    force config/default.json

$ npm start

http://localhost:3030/messages

$ curl 'http://localhost:3030/messages/' -H 'Content-Type: application/json' --data-binary '{ "name": "Curler", "text": "Hello from the command line!" }'

2.3 Adding authentication

Generating authentication :

$ feathers generate authentication

? What authentication providers do you want to use? Other PassportJS strategies 
not in this list can still be configured manually. Username + Password (Local), 
Auth0, Google, Facebook, GitHub
? What is the name of the user (entity) service? users
? What kind of service is it? NeDB

Creating a user and logging in

$ curl 'http://localhost:3030/users/' -H 'Content-Type: application/json' --data-binary '{ "email": "feathers@example.com", "password": "secret" }'

{"email":"feathers@example.com","_id":"fuF1BNK5k0ieuQOZ"}

$ curl 'http://localhost:3030/authentication/' -H 'Content-Type: application/json' --data-binary '{ "strategy": "local", "email": "feathers@example.com", "password": "secret" }'

{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6ImFjY2VzcyJ9.eyJ1c2VySWQiOiJmdUYxQk5LNWswaWV1UU9aIiwiaWF0IjoxNTE3NTc1NjkyLCJleHAiOjE1MTc2NjIwOTIsImF1ZCI6Imh0dHBzOi8veW91cmRvbWFpbi5jb20iLCJpc3MiOiJmZWF0aGVycyIsInN1YiI6ImFub255bW91cyIsImp0aSI6IjM1M2Q0MjVhLTcxOGItNGI5Yy04ZmE4LTA2ZTY3MWJmZTdmMyJ9.feGAxy4aeIUeEMvVygt6DsMFkqcvLOMF4kriQpeoIDs"}

Securing the messages service : update src/services/messages/messages.hooks.js

const { authenticate } = require('@feathersjs/authentication').hooks;

module.exports = {
  before: {
    all: [ authenticate('jwt') ],
    find: [],
Securing real-time events : make sure that real-time service events are only sent to connections that are allowed to see them - for example when users join a specific chat room or one-to-one messages.

src/channels.js

2.4 Processing data

    sanitize the input we get from the client and add additional information.

Sanitizing new message
$ feathers generate hook

? What is the name of the hook? process-message
? What kind of hook should it be? before
? What service(s) should this hook be for (select none to add it yourself)?
 messages
? What methods should the hook be for (select none to add it yourself)? create
   Update src/hooks/process-message.js


Adding a user avatar : add a link to the gravatar images of the user's email address.

$ feathers generate hook

? What is the name of the hook? gravatar
? What kind of hook should it be? before
? What service(s) should this hook be for (select none to add it yourself)?
 users
? What methods should the hook be for (select none to add it yourself)? create
identical src/hooks/gravatar.js
identical test/hooks/gravatar.test.js
   Update src/hooks/gravatar.js


Populating the message sender : In order to show the right user information we want to include that information in our messages.

$ feathers generate hook

? What is the name of the hook? populate-user
? What kind of hook should it be? after
? What service(s) should this hook be for (select none to add it yourself)?
 messages
? What methods should the hook be for (select none to add it yourself)? all
    Update src/hooks/populate-user.js


   We now have a complete API to send and retrieve messages including authentication.

2.5 Building a frontend

   Feathers works great in the browser and comes with client services that allow to easily connect to a Feathers server.

   public/index.html

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <meta name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0" />
    <title>Vanilla JavaScript Feathers Chat</title>
    <link rel="shortcut icon" href="favicon.ico">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.2.0/public/base.css">
    <link rel="stylesheet" href="//cdn.rawgit.com/feathersjs/feathers-chat/v0.2.0/public/chat.css">
  </head>
  <body>
    <div id="app" class="flex flex-column"></div>
    <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.js"></script>
    <script src="//unpkg.com/@feathersjs/client@^3.0.0/dist/feathers.js"></script>
    </script>
    <script src="/socket.io/socket.io.js"></script>
    <script src="app.js"></script>
  </body>
</html>
     public/app.js

    Base and user/message list HTML : 
    Displaying the login/signup or chat page : 

    Login and signup : 

    Real-time and sending message : 

// Establish a Socket.io connection
const socket = io();
// Initialize our Feathers client application through Socket.io
// With hooks and authentication
const client = feathers();

client.configure(feathers.socketio(socket));
// Use localStorage to store our login token
client.configure(feathers.authentication({
   storage: window.localStorage
}));

// Login screen
const loginHTML = `<main class="login container">
  <div class="row">
    <div class="col-12 col-6-tablet push-3-tablet text-center heading">
      <h1 class="font-100">Log in or signup</h1>
    </div>
  </div>
  <div class="row">
    <div class="col-12 col-6-tablet push-3-tablet col-4-desktop push-4-desktop">
      <form class="form">
        <fieldset>
          <input class="block" type="email" name="email" placeholder="email">
        </fieldset>

        <fieldset>
          <input class="block" type="password" name="password" placeholder="password">
        </fieldset>

        <button type="button" id="login" class="button button-primary block signup">
          Log in
        </button>

        <button type="button" id="signup" class="button button-primary block signup">
          Sign up and log in
        </button>
      </form>
    </div>
  </div>
</main>`;

// Chat base HTML (without user list and messages)
const chatHTML = `<main class="flex flex-column">
  <header class="title-bar flex flex-row flex-center">
    <div class="title-wrapper block center-element">
      <img class="logo" src="http://feathersjs.com/img/feathers-logo-wide.png"
        alt="Feathers Logo">
      <span class="title">Chat</span>
    </div>
  </header>

  <div class="flex flex-row flex-1 clear">
    <aside class="sidebar col col-3 flex flex-column flex-space-between">
      <header class="flex flex-row flex-center">
        <h4 class="font-300 text-center">
          <span class="font-600 online-count">0</span> users
        </h4>
      </header>

      <ul class="flex flex-column flex-1 list-unstyled user-list"></ul>
      <footer class="flex flex-row flex-center">
        <a href="#" id="logout" class="button button-primary">
          Sign Out
        </a>
      </footer>
    </aside>

    <div class="flex flex-column col col-9">
      <main class="chat flex flex-column flex-1 clear"></main>

      <form class="flex flex-row flex-space-between" id="send-message">
        <input type="text" name="text" class="flex flex-1">
        <button class="button-primary" type="submit">Send</button>
      </form>
    </div>
  </div>
</main>`;

// Add a new user to the list
const addUser = user => {
	const userList = document.querySelector('.user-list');
	if (userList) {
		// Add the user to the list
		userList.insertAdjacentHTML('beforeend', `<li>
      <a class="block relative" href="#">
        <img src="${user.avatar}" alt="" class="avatar">
        <span class="absolute username">${user.email}</span>
      </a>
     </li>`);

	  // Update the number of users
	  const userCount = document.querySelectorAll('.user-list li').length;
	  document.querySelector('.online-count').innerHTML = userCount;
	}
};

// Renders a new message and finds the user that belongs to the message
const addMessage = message => {
	// Find the user belonging to this message or use the anonymous user if not found
	const { user = {} } = message;
	const chat = document.querySelector('.chat');
	// Escape HTML
	const text = message.text
	  .replace(/&/g, '&')
	  .replace(/</g, '<').replace(/>/g, '>');

	if (chat) {
		chat.insertAdjacentHTML( 'beforeend', `<div class="message flex flex-row">
      <img src="${user.avatar}" alt="${user.email}" class="avatar">
      <div class="message-wrapper">
        <p class="message-header">
          <span class="username font-600">${user.email}</span>
          <span class="sent-date font-300">${moment(message.createdAt).format('MMM Do, hh:mm:ss')}</span>
        </p>
        <p class="message-content font-300">${text}</p>
       </div>
      </div>` );
	  chat.scrollTop = chat.scrollHeight - chat.clientHeight;
    }
};

// Show the login page
const showLogin = (error = {}) => {
	if (document.querySelectorAll('.login').length) {
		document.querySelector('.heading').insertAdjacentHTML('beforeend', `<p>There was an error: ${error.message}</p>`);
	} else {
		document.getElementById('app').innerHTML = loginHTML;
	}
};

// Shows the chat page
const showChat = async () => {
	document.getElementById('app').innerHTML = chatHTML;
	// Find the latest 25 messages
	const messages = await client.service('messages').find({
		query: {
			$sort: { createdAt: -1 },
			$limit: 25
		}
	});
	// We want to show the newest message last
	messages.data.reverse().forEach(addMessage);
	// Find all users
	const users = await client.service('users').find();
	users.data.forEach(addUser);
};

// Retrieve email/password object from the login/signup page
const getCredentials = () => {
	const user = {
		email: document.querySelector('[name="email"]').value,
		password: document.querySelector('[name="password"]').value
	};
	return user;
};
// Log in either using the given email/password or the token from storage
const login = async credentials => {
	try {
		if (!credentials) {
			// Try to authenticate using the JWT from localStorage
			await client.authenticate();
		} else {
			// If we get login information, add the strategy we want to use for login
			const payload = object.assign({ strategy: 'local' }, credentials );
			await client.authenticate(payload);
		}
		// If successful, show the chat page
		showChat();
	} catch(error) {
		// If we got an error, show the login page
		showLogin(error);
	}
};

document.addEventListener('click', async ev => {
	switch(ev.target.id) {
		case 'signup' : {
			// For signup, create a new user and then log them in
			const credentials = getCredentials();
			// First create the user
			await client.service('users').create(credentials);
			// If successful log them in
			await login(credentials);
			break;
		}
		case 'login' : {
			const user = getCredentials();
			await login(user);
			break;
		}
		case 'logout' : {
			await client.logout();
			document.getElementById('app').innerHTML = loginHTML;
			break;
		}
	}
});

// send new message and make the user and message list update in real-time
document.addEventListener('submit', async ev => {
	if (ev.target.id === 'send-message') {
		// This is the message text input field
		const input = document.querySelector('[name="text"]');
		ev.preventDefault();
		// Create a new message and then clear the input field
		await client.service('messages').create({
			text: input.value
		});
		input.value = '';
	}
});

// Listen to created events and add the new message in real-time
client.service('messages').on('created', addMessage);
// We will also see when new users get created in real-time
client.service('users').on('created', addUser);
login();

2.6 Writing tests

      test


 类似资料: