Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Current »

Document Control

Version

Description

Date

1.0.0

Init document

30 Jan 2024

I. Overview

xStreamer comes with 3 big parts

  • API server

  • Frontend web server (front office, user)

  • Backend web server (back office, admin)

II. Details

1. Api

Api server uses these frameworks / softwares on the top:

  • NestJS as a main framework

  • DB: MongoDB

  • Cache: Redis

  • Messaging: Redis

  • Queue: Redis

1.1 Folder Structures

src
--config
--kernel
--modules
----config
----controllers
----services
----schemas
----payloads
--scripts
templates
----emails
------contact.html
------email-verification.html
------other-email-templates.html
--main.ts
--app.service.ts
--app.controller.ts
--app.module.ts
--script.ts
test
views
--custom-view
public
--avatars
--images
--...
.env
env.example
package.json
tsconfig.json

With structures above we can see xStreamer split applications to smaller modules, each module is responsible the app logic

Modules

Description

Auth

Service provides APIs to execute all use-cases related to authentication and authorization such as Sign In, Sign Out, Access Token Verification and User's Roles and Permissions and etc.

User

Service provides APIs to execute all use-cases related to user domain such as Get, Edit Profile and etc

Payment

Service provides APIs to execute all use-cases related to payment such as CCBill payment, payment history…

File

Service provides APIs to execute all use-cases related to user domain such as convert file, create thumbnails, save file to protected folder, get file details…

Mailer

Service provides APIs to execute all use-cases related to send mail such as create content from template, send mail in queue, etc…

Message

Service provides APIs to execute all use-cases related to message such as public chat, group chat, private chat

Performer

Service provides APIs to execute all use-cases related to performer domain such as Get, Edit Profile and etc

Assets

Service provides APIs to execute all use-cases related to performer domain such as Get, Edit, Upload media data, etc…

Stream

Service provides APIs to execute all use-cases related to stream such as create connection to ant media

Socket

Service provides method to communicate between server and browser in with real time message

Settings

Service provides APIs to execute all user-cases related to settings such as update config, get public config

Statistic

Service provides API to execute all user-cases related to statistic

Utils

Service provides common APIs which using among services

 

1.2 Module structure

A general module will come with below structure.

  • controllers: provide Restful Apis to external / internal eg: GET /users/:userId to get user info

  • dtos: acronym of Data Transfer Object. It is an object which helps transfer data from request to database, among services, etc…

  • exceptions: contains http exceptions

  • listeners: listen and handle business logic when receive an event (like messaging listener service)

  • models: define Mongoose database model

  • payloads: define request payload from external to server and validate with class validator

  • providers: Providers are a fundamental concept in Nest. Many of the basic Nest classes may be treated as a provider – services, repositories, factories, helpers, and so on. The main idea of a provider is that it can inject dependencies; this means objects can create various relationships with each other, and the function of "wiring up" instances of objects can largely be delegated to the Nest runtime system.

  • schemas: provide mongo db schemas

  • services: service will be responsible for data storage and retrieval, provide method to communicate with another service

  • validators: provide custom class-validator methods

Example for user module

controllers
--user.controller.ts
--admin-user.controller.ts
dtos
--user.dto.ts
--index.ts
exceptions
--account-not-found.exception.ts
--index.ts
listeners
--user-connected.listener.ts
--index.ts
models
--user.model.ts
--index.ts
payloads
--user-create.payload.ts
--user-update.payload.ts
--...
providers
--user.providers.ts
--index.ts
schema
--user.schema.ts
--index.ts
services
--user.service.ts
--user-search.service.ts
--index.ts
validators
--username.validator.ts
constants.ts
user.module.ts

2. Frontend web

Frontend web use NextJS as a main framework, TailwindCSS for styling

2.1 Folder structure

src
--app
---layout.tsx
---account
-----page.tsx
---videos
----page.tsx
--components
--interfaces
--lib
@types
style
--default.scss
public
--fonts
--icons
--sounds
--image-file.png
--...

#

Description

src

Contains React components, Redux storage, Socket handler which we use in the app. Written with typescript

Check here for nextJS structure

style

Provide definition of style in the app such as color, font weight…

public

Contains public static files such as icons, images, sounds

3. Backend web

Frontend web use NextJS as a main framework, Ant design for UI components with structure is same Frontend web

III. Sample code snipplets

  1. Login API

import {
  Body, Controller, Post, UsePipes, ValidationPipe
} from '@nestjs/common';
import { DataResponse } from 'src/core';
import { LoginPasswordPayload } from 'src/payloads';
import { AuthService } from 'src/services';

@Controller('/auth/login')
export class LoginController {
  constructor(
    private authService: AuthService
  ) {}

  @Post()
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  async register(
    @Body() payload: LoginPasswordPayload
  ) {
    const res = await this.authService.loginWithPassword(payload);
    return DataResponse.ok(res);
  }
}
  1. Login form

'use client';

import Link from 'next/link';
import { Formik, Form, Field, FieldProps } from 'formik';
import { signIn } from 'next-auth/react';
import { useTranslation } from 'react-i18next';
import * as yup from 'yup';
import { FormItem, Input } from '@components/ui/form';
import { useState } from 'react';
import { FormattedMessage } from '@components/i18n';
import { useMainThemeLayout } from 'src/providers/main-layout.provider';

const schema = yup.object().shape({
  username: yup.string().required('Username is required!'),
  password: yup.string().required('Password is required!')
});

export default function Login() {
  const { t } = useTranslation();
  const [error, setError] = useState<string | null>();
  const { register, forgotPassword, closePopup } = useMainThemeLayout();


  return (
    <div className="w-full space-y-5 p-10 text-center">
      <Link href="/" className="font-bold text-4xl text-center">
        xStreamer
      </Link>
      <h2 className="text-center text-2xl font-bold leading-9 tracking-tigh">
        {t('signin', 'Sign in')}
      </h2>
      <Formik
        initialValues={{ username: '', password: '' }}
        validationSchema={schema}
        onSubmit={async (values, actions) => {
          const res = await signIn('credentials', {
            ...values,
            redirect: false
          });

          if (res && res.ok) {
            closePopup();
            // loadProfile();
            return;
          }

          setError(res!.error);
          actions.setSubmitting(false);
        }}
      >
        {({ handleSubmit }) => (
          <Form className="space-y-3" onSubmit={handleSubmit}>
            <Field name="username">
              {({ field, meta }: FieldProps) => (
                <FormItem>
                  <Input placeholder="Username" {...field} />
                  {meta.touched && !!meta.error && (
                    <FormItem.Error>{meta.error}</FormItem.Error>
                  )}
                </FormItem>
              )}
            </Field>

            <div>
              <Field name="password">
                {({ field, meta }: FieldProps) => (
                  <FormItem>
                    <Input type="password" placeholder="Password" {...field} />
                    {meta.touched && !!meta.error && (
                      <FormItem.Error>{meta.error}</FormItem.Error>
                    )}
                  </FormItem>
                )}
              </Field>
            </div>
            <div className="flex items-center justify-between">
              <div className="text-sm text-primary">
                <button onClick={forgotPassword} role="button" type="button">
                  <FormattedMessage
                    id="forgotPassword"
                    defaultValue="Forgot password"
                    subifx="?"
                  />
                </button>
              </div>
            </div>
            {error && (
              <div
                className="bg-red-100 dark:bg-red-950  border border-red-900 text-red-700 dark:text-white px-4 py-3 rounded relative"
                role="alert"
              >
                <span className="text-sm font-medium">{error}</span>
              </div>
            )}

            <div className="text-center">
              <button
                type="submit"
                className="rounded-md bg-orange-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-orange-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-600"
              >
                {t('signin', 'Sign in')}
              </button>
            </div>
          </Form>
        )}
      </Formik>

      <p className="mt-10 text-center text-sm text-gray-500">
        Don&apos;t have an account?
        {' '}
        <a
          onClick={register}
          className="font-semibold leading-6 cursor-pointer hover:text-primary"
        >
          {t('signup', 'Sign Up')}
        </a>
      </p>
    </div>
  );
}
  • No labels