├── .browserslistrc ├── cypress.json ├── babel.config.js ├── src ├── shims-vue.d.ts ├── utils │ ├── validate │ │ ├── index.ts │ │ ├── validators.ts │ │ └── createValidateSchema.ts │ ├── formatDate │ │ └── formatDate.ts │ ├── debounce │ │ └── debounce.ts │ ├── yandexUrl │ │ └── getYandexUrl.ts │ └── persist │ │ └── persist.ts ├── types │ ├── routes.ts │ ├── events.ts │ └── chats.ts ├── shims-vuetify.d.ts ├── assets │ └── images │ │ └── default-avatar.jpg ├── plugins │ └── vuetify.ts ├── store │ ├── store.ts │ ├── index.ts │ └── modules │ │ ├── auth │ │ └── auth.ts │ │ └── chats │ │ └── chats.ts ├── constants │ └── index.ts ├── shims-tsx.d.ts ├── App.vue ├── main.ts ├── api │ ├── base │ │ ├── generate.js │ │ └── swagger.yaml │ ├── chats-service.ts │ ├── user-service.ts │ ├── response-handler.ts │ └── generated │ │ └── generated-service.ts ├── components │ ├── chats.vue │ ├── chat.vue │ └── virtual-scroll.vue ├── router │ └── index.ts └── views │ ├── chats-view.vue │ ├── chat-create-view.vue │ ├── signin-view.vue │ └── signup-view.vue ├── jest.config.js ├── public ├── favicon.ico └── index.html ├── .editorconfig ├── .gitignore ├── vue.config.js ├── .eslintrc.js ├── tsconfig.json ├── README.md └── package.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | 4 | export default Vue; 5 | } 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 3 | }; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvar91/ozon-tech-hw6-vue-vuex-tsx-vuetify/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/utils/validate/index.ts: -------------------------------------------------------------------------------- 1 | export { schema } from "./createValidateSchema"; 2 | export { validators } from "./validators"; 3 | -------------------------------------------------------------------------------- /src/types/routes.ts: -------------------------------------------------------------------------------- 1 | export interface Route { 2 | title: string; 3 | to: string; 4 | } 5 | 6 | export type Routes = Route[]; 7 | -------------------------------------------------------------------------------- /src/shims-vuetify.d.ts: -------------------------------------------------------------------------------- 1 | declare module "vuetify/lib/framework" { 2 | import Vuetify from "vuetify"; 3 | export default Vuetify; 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/images/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alvar91/ozon-tech-hw6-vue-vuex-tsx-vuetify/HEAD/src/assets/images/default-avatar.jpg -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuetify from 'vuetify/lib/framework'; 3 | 4 | Vue.use(Vuetify); 5 | 6 | export default new Vuetify(); 7 | -------------------------------------------------------------------------------- /src/types/events.ts: -------------------------------------------------------------------------------- 1 | export interface HandleInputChange extends Event { 2 | target: HTMLInputElement; 3 | } 4 | 5 | export interface HandleChange extends Event { 6 | target: HTMLElement; 7 | } 8 | -------------------------------------------------------------------------------- /src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "vuex-simple"; 2 | import { AuthModule } from "./modules/auth/auth"; 3 | import { ChatsModule } from "./modules/chats/chats"; 4 | 5 | export class Store { 6 | @Module() 7 | public auth = new AuthModule(); 8 | 9 | @Module() 10 | public chats = new ChatsModule(); 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | export enum ROUTES_TO { 2 | // signin = "/auth/signin", 3 | signin = "/", 4 | signup = "/auth/signup", 5 | logout = "/auth/logout", 6 | chats = "/chats", 7 | createChat = "/chats/create", 8 | } 9 | 10 | export const FIRST_PAGE = 1; 11 | 12 | export const PERSISTED_STORAGE_NAME = "OZON_CHATS_JOURNEY_MAP"; 13 | -------------------------------------------------------------------------------- /src/utils/formatDate/formatDate.ts: -------------------------------------------------------------------------------- 1 | function padTo2Digits(num: number): string { 2 | return num.toString().padStart(2, "0"); 3 | } 4 | 5 | export function formatDate(date: Date): string { 6 | return [ 7 | padTo2Digits(date.getDate()), 8 | padTo2Digits(date.getMonth() + 1), 9 | date.getFullYear(), 10 | ].join("."); 11 | } 12 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import { createVuexStore } from "vuex-simple"; 4 | 5 | import { Store } from './store'; 6 | 7 | Vue.use(Vuex); 8 | 9 | const store = createVuexStore(new Store(), { 10 | strict: false, 11 | modules: {}, 12 | plugins: [], 13 | }); 14 | 15 | export default store; 16 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("@vue/cli-service"); 2 | module.exports = defineConfig({ 3 | transpileDependencies: ["vuetify"], 4 | devServer: { 5 | proxy: { 6 | "^/api/v2": { 7 | target: "http://62.113.98.233:5000", 8 | secure: false, 9 | changeOrigin: true, 10 | ws: true, 11 | }, 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "@/App.vue"; 3 | import vuetify from "@/plugins/vuetify"; 4 | 5 | import router from "@/router"; 6 | import store from "@/store"; 7 | 8 | import Notifications from "vue-notification"; 9 | 10 | Vue.config.productionTip = false; 11 | Vue.use(Notifications); 12 | 13 | new Vue({ 14 | vuetify, 15 | store, 16 | router, 17 | render: (h) => h(App), 18 | }).$mount("#app"); 19 | -------------------------------------------------------------------------------- /src/types/chats.ts: -------------------------------------------------------------------------------- 1 | export interface LastMessage { 2 | user: { 3 | first_name: string; 4 | second_name: string; 5 | avatar: string; 6 | email: string; 7 | login: string; 8 | phone: string; 9 | }; 10 | time: string; 11 | content: string; 12 | } 13 | export interface Chat { 14 | id: number; 15 | title: string; 16 | avatar: string; 17 | unread_count: number; 18 | last_message: LastMessage; 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/debounce/debounce.ts: -------------------------------------------------------------------------------- 1 | export function Debounce(timeout: number) { 2 | let timeoutRef; 3 | 4 | return function ( 5 | target, 6 | propertyKey: string, 7 | descriptor: PropertyDescriptor 8 | ) { 9 | const original = descriptor.value; 10 | 11 | descriptor.value = function (...args) { 12 | clearTimeout(timeoutRef); 13 | 14 | timeoutRef = setTimeout(() => { 15 | original.apply(this, args); 16 | }, timeout); 17 | }; 18 | 19 | return descriptor; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/utils/yandexUrl/getYandexUrl.ts: -------------------------------------------------------------------------------- 1 | const VUE_APP_YANDEX_OAUTH2_CLIENT_ID = "c98baded858640108b2a74710f9b9a20"; 2 | export const VUE_APP_YANDEX_OAUTH2_REDIRECT = "http://localhost:5000/"; 3 | 4 | export const getYandexUrl = (from) => { 5 | const rootUrl = `https://oauth.yandex.ru/authorize?`; 6 | 7 | const options: any = { 8 | redirect_uri: VUE_APP_YANDEX_OAUTH2_REDIRECT, 9 | client_id: VUE_APP_YANDEX_OAUTH2_CLIENT_ID, 10 | response_type: "code", 11 | force_confirm: true, 12 | state: from, 13 | }; 14 | 15 | const qs = new URLSearchParams(options); 16 | 17 | return `${rootUrl}${qs.toString()}`; 18 | }; 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: ["@vue/typescript", "plugin:vue/essential", "eslint:recommended"], 7 | parserOptions: { 8 | ecmaFeatures: { 9 | legacyDecorators: true, 10 | }, 11 | }, 12 | rules: { 13 | "vue/multi-word-component-names": 0, 14 | "no-unused-vars": "off", 15 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 16 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 17 | }, 18 | overrides: [ 19 | { 20 | files: ["**/__tests__/*.{j,t}s?(x)", "**/tests/unit/**/*.spec.{j,t}s?(x)"], 21 | env: { 22 | jest: true, 23 | }, 24 | }, 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /src/utils/persist/persist.ts: -------------------------------------------------------------------------------- 1 | import { PERSISTED_STORAGE_NAME } from "@/constants"; 2 | 3 | export class PersistStorage { 4 | static initiateStorage() { 5 | localStorage.setItem(PERSISTED_STORAGE_NAME, JSON.stringify({})); 6 | } 7 | 8 | static getStorage() { 9 | let storage = localStorage.getItem(PERSISTED_STORAGE_NAME); 10 | 11 | if (!storage) { 12 | this.initiateStorage(); 13 | storage = localStorage.getItem(PERSISTED_STORAGE_NAME); 14 | } 15 | 16 | return JSON.parse(storage as string); 17 | } 18 | 19 | static setStorage(path) { 20 | const storage = this.getStorage(); 21 | 22 | if (!storage[path]) storage[path] = []; 23 | 24 | storage[path].push({ path, date: new Date() }); 25 | 26 | localStorage.setItem(PERSISTED_STORAGE_NAME, JSON.stringify(storage)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/base/generate.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { generateApi } = require("swagger-typescript-api"); 3 | 4 | /* NOTE: all fields are optional expect one of `output`, `url`, `spec` */ 5 | generateApi({ 6 | // имя генерируемого файла 7 | name: "generated-service.ts", 8 | // путь, куда будет сохранен генерируемый файл 9 | output: path.resolve(process.cwd(), "../generated"), 10 | // источник 11 | // url (для ссылки на swagger.json в сети) или input (для файла swagger.json или swagger.yaml в проекте) 12 | // url: 'http://localhost:8080/swagger.json', 13 | input: path.resolve(process.cwd(), "./swagger.yaml"), 14 | // http клиент: fetch или axios 15 | httpClientType: "axios", 16 | generateResponses: true, 17 | generateClient: true, 18 | enumNamesAsValues: false, 19 | }); 20 | 21 | // для запуска, выполнить "node generate.js" в терминали в папке с этим файлом 22 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/api/chats-service.ts: -------------------------------------------------------------------------------- 1 | import { Api, CreateChatRequest } from "./generated/generated-service"; 2 | 3 | import { WithErrorNotification } from "./response-handler"; 4 | 5 | const getOptions = ({ pageNumber = 1, title = "", step = 10 }) => { 6 | return { 7 | offset: step * pageNumber, 8 | limit: step, 9 | title, 10 | }; 11 | }; 12 | 13 | export class ChatsService { 14 | static chatsService = new Api().chats; 15 | 16 | @WithErrorNotification({ 17 | errorMessage: "Не удалось получить список чатов!", 18 | }) 19 | static async fetchChats(pageNumber: number, title: string, step?: number) { 20 | return await this.chatsService.chatsList(getOptions({ pageNumber, title, step })); 21 | } 22 | 23 | @WithErrorNotification({ 24 | errorMessage: "Не удалось создать чат!", 25 | }) 26 | static async createChat(data: CreateChatRequest) { 27 | return await this.chatsService.chatsCreate(data); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/validate/validators.ts: -------------------------------------------------------------------------------- 1 | import { 2 | required, 3 | requiredIf, 4 | alpha, 5 | email, 6 | numeric, 7 | helpers, 8 | } from "vuelidate/lib/validators"; 9 | 10 | export const validators = { 11 | required, 12 | requiredIf, 13 | alpha, 14 | alphaRus: helpers.regex("alphaRus", /^[а-яё]*$/i), 15 | numeric, 16 | email, 17 | phoneLength: (value) => value.length === 17, 18 | }; 19 | 20 | const errorMessages = { 21 | required: "Обязательное поле", 22 | alpha: "Только латинские буквы", 23 | alphaRus: "Только русские буквы", 24 | numeric: "Только цифры", 25 | email: "Неправильный формат электронной почты", 26 | phoneLength: "Заполните номер телефона", 27 | }; 28 | 29 | export const getErrorMessages = (errors) => { 30 | return Object.entries(errors).reduce((acc: any, [key, value]) => { 31 | if (value) { 32 | const message = 33 | (errorMessages[key] as string) || "Неправильно заполнено поле"; 34 | acc.push(message); 35 | } 36 | return acc; 37 | }, []); 38 | }; 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "useDefineForClassFields": true, 15 | "sourceMap": true, 16 | "resolveJsonModule": true, 17 | "baseUrl": ".", 18 | "types": [ 19 | "webpack-env", 20 | "jest", 21 | "vuetify" 22 | ], 23 | "paths": { 24 | "@/*": [ 25 | "src/*" 26 | ] 27 | }, 28 | "lib": [ 29 | "esnext", 30 | "dom", 31 | "dom.iterable", 32 | "scripthost" 33 | ] 34 | }, 35 | "include": [ 36 | "src/**/*.ts", 37 | "src/**/*.tsx", 38 | "src/**/*.vue", 39 | "tests/**/*.ts", 40 | "tests/**/*.tsx" 41 | , "src/router/index.ts", "src/main.ts" ], 42 | "exclude": [ 43 | "node_modules" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /src/api/user-service.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Api, 3 | SignUpRequest, 4 | SignInRequest, 5 | OauthSignInRequest, 6 | } from "./generated/generated-service"; 7 | 8 | import { WithErrorNotification } from "./response-handler"; 9 | export class UserService { 10 | static userService = new Api().auth; 11 | static userOauthService = new Api().oauth; 12 | 13 | @WithErrorNotification({ 14 | errorMessage: "Не удалось создать пользователя!", 15 | }) 16 | static async createUser(data: SignUpRequest) { 17 | return await this.userService.signupCreate(data); 18 | } 19 | 20 | @WithErrorNotification({ 21 | errorMessage: "Не удалось войти!", 22 | }) 23 | static async logIn(data: SignInRequest) { 24 | return await this.userService.signinCreate(data); 25 | } 26 | 27 | @WithErrorNotification({ 28 | errorMessage: "Не удалось выйти!", 29 | }) 30 | static async logOut() { 31 | return await this.userService.logoutCreate(); 32 | } 33 | 34 | @WithErrorNotification({ 35 | errorMessage: "Не удалось войти!", 36 | }) 37 | static async logInYandex(data: OauthSignInRequest) { 38 | return await this.userOauthService.yandexCreate(data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/store/modules/auth/auth.ts: -------------------------------------------------------------------------------- 1 | import { Action } from "vuex-simple"; 2 | 3 | import { UserService } from "@/api/user-service"; 4 | 5 | import router from "@/router"; 6 | import { ROUTES_TO, FIRST_PAGE } from "@/constants/index"; 7 | 8 | export class AuthModule { 9 | @Action() 10 | async createUser(data) { 11 | const createUserResponse = await UserService.createUser(data); 12 | if (createUserResponse?.status === 200) { 13 | localStorage.isAuthenticated = true; 14 | router.push(`${ROUTES_TO.chats}`); 15 | } 16 | } 17 | 18 | @Action() 19 | async logIn(data) { 20 | const loginResponse = await UserService.logIn(data); 21 | if (loginResponse?.status === 200) { 22 | localStorage.isAuthenticated = true; 23 | router.push(`${ROUTES_TO.chats}`); 24 | } 25 | } 26 | 27 | @Action() 28 | async logOut() { 29 | const response = await UserService.logOut(); 30 | 31 | if (response?.status === 200) { 32 | localStorage.removeItem("isAuthenticated"); 33 | router.push({ name: "SigninView" }); 34 | } 35 | } 36 | 37 | @Action() 38 | async logInYandex(data) { 39 | const response = await UserService.logInYandex(data); 40 | 41 | if (response?.status === 200) { 42 | localStorage.isAuthenticated = true; 43 | router.push(`${ROUTES_TO.chats}`); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/validate/createValidateSchema.ts: -------------------------------------------------------------------------------- 1 | import { getErrorMessages } from "./validators"; 2 | 3 | const isFieldDirty = (field) => field?.$dirty; 4 | 5 | const validate = (field, errors) => { 6 | return isFieldDirty(field) ? getErrorMessages(errors) : []; 7 | }; 8 | 9 | const validateLogin = (field) => 10 | validate(field, { 11 | required: !field.required, 12 | }); 13 | 14 | const validatePassword = (field) => 15 | validate(field, { 16 | required: !field.required, 17 | }); 18 | 19 | const validateFirstName = (field) => 20 | validate(field, { 21 | required: !field.required, 22 | alphaRus: !field.alphaRus, 23 | }); 24 | 25 | const validateSecondName = (field) => 26 | validate(field, { 27 | required: !field.required, 28 | alphaRus: !field.alphaRus, 29 | }); 30 | 31 | const validateEmail = (field) => 32 | validate(field, { 33 | required: !field.required, 34 | email: !field.email, 35 | }); 36 | 37 | const validatePhone = (field) => 38 | validate(field, { 39 | required: !field.required, 40 | phoneLength: !field.phoneLength, 41 | }); 42 | 43 | const validateChatTitle = (field) => 44 | validate(field, { 45 | required: !field.required, 46 | }); 47 | 48 | export const schema = { 49 | validateLogin, 50 | validatePassword, 51 | validateFirstName, 52 | validateSecondName, 53 | validateEmail, 54 | validatePhone, 55 | validateChatTitle, 56 | }; 57 | -------------------------------------------------------------------------------- /src/components/chats.vue: -------------------------------------------------------------------------------- 1 | 57 | -------------------------------------------------------------------------------- /src/api/response-handler.ts: -------------------------------------------------------------------------------- 1 | import { AxiosError } from "axios"; 2 | import router from "@/router"; 3 | 4 | interface Options { 5 | errorMessage?: string; 6 | defaultValue?: R | undefined; 7 | } 8 | 9 | const showErrorNotification = (error: Error, message?: string) => { 10 | const text = message || error.message || "Что-то пошло не так!"; 11 | 12 | alert(text); 13 | }; 14 | 15 | const HandleErrorDecorator = ( 16 | handler: (error: AxiosError, message?: string) => void, 17 | defaultValue?: R | undefined, 18 | message?: string 19 | ) => { 20 | return (target: unknown, key: string, descriptor: PropertyDescriptor) => { 21 | const original = descriptor.value; 22 | 23 | descriptor.value = async function (...args: unknown[]) { 24 | try { 25 | return await original.apply(this, args); 26 | } catch (error) { 27 | handler(error as AxiosError, message); 28 | return defaultValue; 29 | } 30 | }; 31 | 32 | return descriptor; 33 | }; 34 | }; 35 | 36 | export const WithErrorNotification = ({ 37 | errorMessage, 38 | defaultValue, 39 | }: Options) => { 40 | return HandleErrorDecorator( 41 | (error, message?: string) => { 42 | if (error?.response?.status === 401) { 43 | localStorage.removeItem("isAuthenticated"); 44 | router.push({ name: "SigninView" }); 45 | } 46 | 47 | showErrorNotification(error, message); 48 | }, 49 | defaultValue, 50 | errorMessage 51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter, { RouteConfig } from "vue-router"; 3 | 4 | import SigninView from "@/views/signin-view.vue"; 5 | 6 | import { ROUTES_TO } from "@/constants"; 7 | 8 | import { PersistStorage } from "@/utils/persist/persist"; 9 | 10 | Vue.use(VueRouter); 11 | 12 | const routes: Array = [ 13 | { 14 | path: ROUTES_TO.signin, 15 | name: "SigninView", 16 | component: SigninView, 17 | }, 18 | { 19 | path: ROUTES_TO.signup, 20 | name: "SignupView", 21 | component: () => import("@/views/signup-view.vue"), 22 | }, 23 | { 24 | path: `${ROUTES_TO.chats}`, 25 | name: "ChatsView", 26 | component: () => import("@/views/chats-view.vue"), 27 | meta: { 28 | requiresAuth: true, 29 | }, 30 | }, 31 | { 32 | path: ROUTES_TO.createChat, 33 | name: "ChatCreateView", 34 | component: () => import("@/views/chat-create-view.vue"), 35 | meta: { 36 | requiresAuth: true, 37 | }, 38 | }, 39 | { 40 | path: "*", 41 | redirect: ROUTES_TO.signin, 42 | }, 43 | ]; 44 | 45 | const router = new VueRouter({ 46 | mode: "history", 47 | routes, 48 | }); 49 | 50 | router.beforeEach((to, from, next) => { 51 | PersistStorage.setStorage(to.path); 52 | 53 | if (to.meta?.requiresAuth) { 54 | if (!localStorage.getItem("isAuthenticated")) { 55 | router.push({ name: "SigninView" }); 56 | } else { 57 | next(); 58 | } 59 | } else { 60 | if (localStorage.getItem("isAuthenticated")) { 61 | router.push(`${ROUTES_TO.chats}`); 62 | } else { 63 | next(); 64 | } 65 | } 66 | }); 67 | 68 | export default router; 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Домашка 6 # 2 | 3 | ## Задание ## 4 | За исходники взять д/з №5 5 | Переписать компоненты с .vue на .tsx 6 | Заменить в таблице пагинацию (из д/з №5) на virtual scroll (взять с воркшопа или реализовать свой) 7 | Реализовать ленивую загрузку для virtual scroll: после отрисовки последнего элемента отправлялся запрос на сервер, для получения следующих элементов списка. Если элементов больше нет, вывести предупреждение, что на данный момент все данные получены. 8 | Доп. задание: 9 | Реализовать фильтрацию в таблице (запрос "/chats", параметр "title") 10 | Добавить Guards: 11 | 1 - для проверки авторизации: сейчас для проверки авторизации используется запрос, необходимо написать Guards, который перед переходом на страницу будет проверять авторизацию с помощью этого запроса 12 | 2 - для логирования посещенных страниц: добавить Guards, который сохраняет в массив список посещенных страниц. Такое может понадобиться для сбора метрик по посещению страниц сайта. Хранить можно в (local/session/cache)Storage 13 | Сгенерировать API и перейти на его использование с декораторами (пример в материалах воркшопа, в директории "api") 14 | Доп. материалы: 15 | для генерации API использовать - https://github.com/acacode/swagger-typescript-api 16 | config для генератора API (конфиурация и инструкция в материалах воркшопа) 17 | 18 | ## Project setup 19 | ``` 20 | yarn install 21 | ``` 22 | 23 | ### Compiles and hot-reloads for development 24 | ``` 25 | yarn serve 26 | ``` 27 | 28 | ### Compiles and minifies for production 29 | ``` 30 | yarn build 31 | ``` 32 | 33 | ### Lints and fixes files 34 | ``` 35 | yarn lint 36 | ``` 37 | 38 | ### Customize configuration 39 | See [Configuration Reference](https://cli.vuejs.org/config/). 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homework-6", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve --port 5000", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "test:e2e": "vue-cli-service test:e2e", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "dependencies": { 13 | "@types/vuelidate": "^0.7.15", 14 | "axios": "^1.1.3", 15 | "core-js": "^3.8.3", 16 | "v-mask": "^2.3.0", 17 | "vue": "^2.6.14", 18 | "vue-class-component": "^7.2.3", 19 | "vue-notification": "^1.3.20", 20 | "vue-property-decorator": "^9.1.2", 21 | "vue-router": "^3.5.3", 22 | "vuelidate": "^0.7.6", 23 | "vuetify": "^2.6.0", 24 | "vuex": "^3.6.2", 25 | "vuex-simple": "^2.2.0" 26 | }, 27 | "devDependencies": { 28 | "@types/jest": "^27.5.2", 29 | "@typescript-eslint/eslint-plugin": "^5.4.0", 30 | "@typescript-eslint/parser": "^5.4.0", 31 | "@vue/cli-plugin-babel": "~5.0.0", 32 | "@vue/cli-plugin-e2e-cypress": "~5.0.0", 33 | "@vue/cli-plugin-eslint": "~5.0.0", 34 | "@vue/cli-plugin-router": "~5.0.0", 35 | "@vue/cli-plugin-typescript": "~5.0.0", 36 | "@vue/cli-plugin-unit-jest": "~5.0.0", 37 | "@vue/cli-plugin-vuex": "~5.0.0", 38 | "@vue/cli-service": "~5.0.0", 39 | "@vue/eslint-config-airbnb": "^6.0.0", 40 | "@vue/eslint-config-prettier": "^7.0.0", 41 | "@vue/eslint-config-standard": "^6.1.0", 42 | "@vue/eslint-config-typescript": "^9.1.0", 43 | "@vue/test-utils": "^1.1.3", 44 | "@vue/vue2-jest": "^27.0.0-alpha.2", 45 | "babel-eslint": "^10.1.0", 46 | "babel-jest": "^27.0.6", 47 | "cypress": "^9.7.0", 48 | "eslint": "^7.32.0", 49 | "eslint-plugin-import": "^2.25.3", 50 | "eslint-plugin-node": "^11.1.0", 51 | "eslint-plugin-promise": "^5.1.0", 52 | "eslint-plugin-vue": "^8.0.3", 53 | "jest": "^27.0.5", 54 | "sass": "~1.32.0", 55 | "sass-loader": "^10.0.0", 56 | "ts-jest": "^27.0.4", 57 | "swagger-typescript-api": "^11.1.1", 58 | "typescript": "~4.5.5", 59 | "vue-cli-plugin-vuetify": "~2.5.8", 60 | "vue-template-compiler": "^2.7.13", 61 | "vuetify-loader": "^1.9.2" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/store/modules/chats/chats.ts: -------------------------------------------------------------------------------- 1 | import { Mutation, State, Action } from "vuex-simple"; 2 | 3 | import { ChatsService } from "@/api/chats-service"; 4 | 5 | import router from "@/router"; 6 | 7 | import { ROUTES_TO, FIRST_PAGE } from "@/constants/index"; 8 | export class ChatsModule { 9 | @State() 10 | chats = []; 11 | 12 | @State() 13 | currentPage = FIRST_PAGE; 14 | 15 | @State() 16 | totalPages = Infinity; 17 | 18 | @State() 19 | filterTitle = ""; 20 | 21 | @Action() 22 | async createChatAction(data) { 23 | const createChatsResponse = await ChatsService.createChat(data); 24 | 25 | if (createChatsResponse?.status === 200) { 26 | router.push(`${ROUTES_TO.chats}`); 27 | } 28 | } 29 | 30 | @Mutation() 31 | setCurrentPage(newPage) { 32 | this.currentPage = newPage; 33 | } 34 | 35 | @Mutation() 36 | setTotalPages(newTotalPages) { 37 | this.totalPages = newTotalPages; 38 | } 39 | 40 | @Mutation() 41 | setFilterTitle(newTitle) { 42 | this.filterTitle = newTitle; 43 | } 44 | 45 | @Mutation() 46 | fetchChats(chats) { 47 | this.chats = chats; 48 | } 49 | 50 | @Mutation() 51 | appendFetchChats(chats) { 52 | this.chats = this.chats.concat(chats); 53 | } 54 | 55 | @Mutation() 56 | resetChatsStore() { 57 | this.chats = []; 58 | this.filterTitle = ""; 59 | this.currentPage = FIRST_PAGE; 60 | } 61 | 62 | @Action() 63 | async fetchChatsAction() { 64 | const pageNumber = FIRST_PAGE; 65 | const title = this.filterTitle; 66 | const createChatsResponse = await ChatsService.fetchChats( 67 | pageNumber - 1, 68 | title 69 | ); 70 | 71 | if (createChatsResponse?.status === 200) { 72 | this.fetchChats(createChatsResponse.data); 73 | this.setCurrentPage(pageNumber); 74 | } 75 | } 76 | 77 | @Action() 78 | async nextFetchChatsAction() { 79 | const pageNumber = this.currentPage + 1; 80 | const title = this.filterTitle; 81 | 82 | const createChatsResponse = await ChatsService.fetchChats( 83 | pageNumber - 1, 84 | title, 85 | ); 86 | 87 | if (createChatsResponse?.status === 200) { 88 | const data = createChatsResponse.data; 89 | if (data.length === 0) { 90 | this.setTotalPages(pageNumber); 91 | } 92 | 93 | this.appendFetchChats(data); 94 | this.setCurrentPage(pageNumber); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/views/chats-view.vue: -------------------------------------------------------------------------------- 1 | 93 | -------------------------------------------------------------------------------- /src/components/chat.vue: -------------------------------------------------------------------------------- 1 | 92 | -------------------------------------------------------------------------------- /src/views/chat-create-view.vue: -------------------------------------------------------------------------------- 1 | 108 | -------------------------------------------------------------------------------- /src/components/virtual-scroll.vue: -------------------------------------------------------------------------------- 1 | 139 | -------------------------------------------------------------------------------- /src/views/signin-view.vue: -------------------------------------------------------------------------------- 1 | 151 | -------------------------------------------------------------------------------- /src/views/signup-view.vue: -------------------------------------------------------------------------------- 1 | 206 | -------------------------------------------------------------------------------- /src/api/generated/generated-service.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | /* 4 | * --------------------------------------------------------------- 5 | * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## 6 | * ## ## 7 | * ## AUTHOR: acacode ## 8 | * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## 9 | * --------------------------------------------------------------- 10 | */ 11 | 12 | /** @example {"id":123,"first_name":"Petya","second_name":"Pupkin","display_name":"Petya Pupkin","login":"userLogin","email":"my@email.com","phone":"89223332211","avatar":"/path/to/avatar.jpg"} */ 13 | export interface UserResponse { 14 | /** User id */ 15 | id: number; 16 | /** First name */ 17 | first_name: string; 18 | /** Second name */ 19 | second_name: string; 20 | /** Display name */ 21 | display_name: string; 22 | /** User login - unique */ 23 | login: string; 24 | /** Email */ 25 | email: string; 26 | /** Phone */ 27 | phone: string; 28 | /** Avatar */ 29 | avatar: string; 30 | } 31 | 32 | export interface SignUpRequest { 33 | /** First name */ 34 | first_name: string; 35 | /** Second name */ 36 | second_name: string; 37 | /** User login - unique */ 38 | login: string; 39 | /** Email /^\S+@\S+$/ */ 40 | email: string; 41 | /** Password */ 42 | password: string; 43 | /** Phone /^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$/ */ 44 | phone: string; 45 | } 46 | 47 | export interface SignInRequest { 48 | /** User login */ 49 | login: string; 50 | /** Password */ 51 | password: string; 52 | } 53 | 54 | export interface SignUpResponse { 55 | /** Created User ID */ 56 | id: number; 57 | } 58 | 59 | export interface CreateChatRequest { 60 | /** Chat title */ 61 | title: string; 62 | } 63 | 64 | export interface UsersRequest { 65 | users: number[]; 66 | /** Chat id */ 67 | chatId: number; 68 | } 69 | 70 | /** @example {"id":123,"title":"my-chat","avatar":"/123/avatar1.jpg","unread_count":15,"last_message":{"user":{"first_name":"Petya","second_name":"Pupkin","avatar":"/path/to/avatar.jpg","email":"my@email.com","login":"userLogin","phone":"8(911)-222-33-22"},"time":"2022-11-02T16:55:02.599Z","content":"this is message content"}} */ 71 | export interface ChatsResponse { 72 | /** Chat id */ 73 | id: number; 74 | /** Chat title */ 75 | title: string; 76 | /** Chat avatar */ 77 | avatar: string; 78 | /** Number of unread messages in chat for current user */ 79 | unread_count: number; 80 | last_message: { 81 | /** 82 | * Message user (sender) 83 | * @format date-time 84 | */ 85 | user?: UserResponse; 86 | /** 87 | * Message timestamp 88 | * @format timestamp 89 | */ 90 | time?: string; 91 | /** Message content */ 92 | content?: string; 93 | }; 94 | } 95 | 96 | export interface ChatDeleteRequest { 97 | /** Chat id */ 98 | chatId: number; 99 | } 100 | 101 | /** @example {"userId":12,"result":{"id":123,"title":"deleted-chat","avatar":"/123/avatar1.jpg"}} */ 102 | export interface ChatDeleteResponse { 103 | /** User id */ 104 | userId: number; 105 | result: ChatsResponse; 106 | } 107 | 108 | export interface ChatArchiveRequest { 109 | /** Chat id */ 110 | chatId: number; 111 | } 112 | 113 | export interface ChatArchiveResponse { 114 | /** User id */ 115 | userId: number; 116 | result: ChatsResponse; 117 | } 118 | 119 | export interface ChatsMessagesTokenResponse { 120 | /** Token for web socket server */ 121 | token: string; 122 | } 123 | 124 | /** @example {"unread_count":12} */ 125 | export interface UnreadCountResponse { 126 | /** New messages count */ 127 | unread_count: number; 128 | } 129 | 130 | export interface LeaderboardNewLeaderRequest { 131 | /** Leaderboard data object, any type */ 132 | data: object; 133 | /** Which field is used to sort (if new value of the field more than old, data is stored) */ 134 | ratingFieldName: string; 135 | /** Your team name. Used to make unique leaderboard for each project. */ 136 | teamName?: string; 137 | } 138 | 139 | export interface LeaderboardRequest { 140 | /** Which field is used to sort */ 141 | ratingFieldName: string; 142 | /** Used to paginate between pages. If limit is 10, then for the 1st page - cursor=0, for the 2nd page - cursor=10. */ 143 | cursor: number; 144 | /** Maximum amount of leaders to return */ 145 | limit: number; 146 | } 147 | 148 | export interface OauthSignInRequest { 149 | /** User code from Yandex */ 150 | code: string; 151 | /** Redirect uri that you are using for oauth */ 152 | redirect_uri: string; 153 | } 154 | 155 | export interface ServiceId { 156 | /** Service id */ 157 | service_id: string; 158 | } 159 | 160 | export interface BadRequestError { 161 | /** Error message */ 162 | reason: string; 163 | } 164 | 165 | export interface UserUpdateRequest { 166 | /** First name */ 167 | first_name: string; 168 | /** Second name */ 169 | second_name: string; 170 | /** Display Name */ 171 | display_name: string; 172 | /** User login - unique */ 173 | login: string; 174 | /** Email */ 175 | email: string; 176 | /** Phone */ 177 | phone: string; 178 | } 179 | 180 | export interface UserRequest { 181 | /** First name */ 182 | first_name: string; 183 | /** Second name */ 184 | second_name: string; 185 | /** Display Name */ 186 | display_name: string; 187 | /** User login - unique */ 188 | login: string; 189 | /** Email */ 190 | email: string; 191 | /** Phone */ 192 | phone: string; 193 | } 194 | 195 | export interface FindUserRequest { 196 | /** User login (beginning of login) */ 197 | login: string; 198 | } 199 | 200 | export interface ChangePasswordRequest { 201 | /** Old password */ 202 | oldPassword: string; 203 | /** New password */ 204 | newPassword: string; 205 | } 206 | 207 | /** @example {"id":123,"user_id":231,"path":"/32543654dsf/434534r3rsddfs_my-file.jpg","filename":"my-file.jpg","content_type":"image/jpeg","content_size":543672,"upload_date":"2022-11-02T16:55:02.599Z"} */ 208 | export interface Resource { 209 | /** Message id */ 210 | id: number; 211 | /** User id */ 212 | user_id: number; 213 | /** Server relative file path */ 214 | path: string; 215 | /** Initial file name */ 216 | filename: string; 217 | /** File content type (e.g "image/jpeg" for .jpg images) */ 218 | content_type: string; 219 | /** File size in bytes */ 220 | content_size: number; 221 | /** 222 | * Resource uploading time 223 | * @format date-time 224 | */ 225 | upload_date: string; 226 | } 227 | 228 | /** @example {"id":123,"user_id":231,"chat_id":312,"time":"2022-11-02T16:55:02.599Z","type":"file","content":132,"file":{"id":132,"user_id":231,"path":"/32543654dsf/434534r3rsddfs_my-file.jpg","filename":"my-file.jpg","content_type":"image/jpeg","content_size":543672,"upload_date":"2022-11-02T16:55:02.599Z"}} */ 229 | export interface ChatMessage { 230 | /** Message id */ 231 | id: number; 232 | /** User id */ 233 | user_id: number; 234 | /** Chat id */ 235 | chat_id: number; 236 | /** 237 | * Message sent time 238 | * @format date-time 239 | */ 240 | time: string; 241 | /** Message type */ 242 | type: "message" | "file"; 243 | /** Message content (message text for messages and resourceId for files) */ 244 | content: string; 245 | /** File */ 246 | file?: Resource; 247 | } 248 | 249 | /** @example {"id":123,"first_name":"petya","second_name":"petrov","display_name":"petya petrov","login":"my-login","email":"my@email.com","phone":"89223332211","avatar":"/path/to/my-file.jpg","role":"admin"} */ 250 | export interface ChatUserResponse { 251 | /** User id */ 252 | id: number; 253 | /** First name */ 254 | first_name: string; 255 | /** Second name */ 256 | second_name: string; 257 | /** Display name */ 258 | display_name: string; 259 | /** User login - unique */ 260 | login: string; 261 | /** Email */ 262 | email: string; 263 | /** Phone */ 264 | phone: string; 265 | /** Avatar */ 266 | avatar: string; 267 | /** User role */ 268 | role: "admin" | "regular"; 269 | } 270 | 271 | export interface StaticChartRequest { 272 | /** Number of points in chart (10 / 100 / 1000) */ 273 | chartSize: "small" | "medium" | "large"; 274 | } 275 | 276 | export interface LiveChartRequest { 277 | /** 278 | * Works as a cursor (initial value should be zero, all the next values are taken from the backend response) 279 | * @format integer 280 | * @default 0 281 | */ 282 | next: number; 283 | } 284 | 285 | export type ChartSchema = { 286 | /** 287 | * X axis (datetime) 288 | * @format date-time 289 | */ 290 | x?: string; 291 | /** @format float */ 292 | y1?: number; 293 | /** @format float */ 294 | y2?: number; 295 | }[]; 296 | 297 | export interface StaticChartResponse { 298 | /** Chart points */ 299 | data?: ChartSchema; 300 | } 301 | 302 | export interface LiveChartResponse { 303 | /** 304 | * Used as a cursor (pass this value to the next request) 305 | * @example 5 306 | */ 307 | next?: number; 308 | /** Chart points */ 309 | data?: ChartSchema; 310 | } 311 | 312 | export interface LiveVideoInfoRequest { 313 | /** 314 | * Works as a cursor (iterate + 1 each request) 315 | * @format integer 316 | * @default 0 317 | */ 318 | iteration: number; 319 | } 320 | 321 | export interface VideoInfoResponse { 322 | /** 323 | * Video size in bytes 324 | * @format integer 325 | * @example 4096 326 | */ 327 | size: number; 328 | } 329 | 330 | export interface Sticker { 331 | /** 332 | * Sticker id (send to chat with WS) 333 | * @format integer 334 | * @example 123 335 | */ 336 | id?: number; 337 | /** 338 | * Url for sticker resource(image) 339 | * @example "/stickers/2346-dfsg-425-sdfs/14534.gif" 340 | */ 341 | path?: string; 342 | } 343 | 344 | export interface StickerPack { 345 | /** 346 | * Sticker pack title 347 | * @example "pack-title" 348 | */ 349 | title?: string; 350 | /** 351 | * User id that created this pack 352 | * @format integer 353 | * @example 123 354 | */ 355 | user_id?: number; 356 | stickers?: string[]; 357 | } 358 | 359 | export interface StickerPacksResponse { 360 | /** StickerPacks */ 361 | data?: StickerPack[]; 362 | } 363 | 364 | export interface StickersResponse { 365 | /** Stickers */ 366 | data?: Sticker[]; 367 | } 368 | 369 | import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from "axios"; 370 | 371 | export type QueryParamsType = Record; 372 | 373 | export interface FullRequestParams extends Omit { 374 | /** set parameter to `true` for call `securityWorker` for this request */ 375 | secure?: boolean; 376 | /** request path */ 377 | path: string; 378 | /** content type of request body */ 379 | type?: ContentType; 380 | /** query params */ 381 | query?: QueryParamsType; 382 | /** format of response (i.e. response.json() -> format: "json") */ 383 | format?: ResponseType; 384 | /** request body */ 385 | body?: unknown; 386 | } 387 | 388 | export type RequestParams = Omit; 389 | 390 | export interface ApiConfig extends Omit { 391 | securityWorker?: ( 392 | securityData: SecurityDataType | null, 393 | ) => Promise | AxiosRequestConfig | void; 394 | secure?: boolean; 395 | format?: ResponseType; 396 | } 397 | 398 | export enum ContentType { 399 | Json = "application/json", 400 | FormData = "multipart/form-data", 401 | UrlEncoded = "application/x-www-form-urlencoded", 402 | } 403 | 404 | export class HttpClient { 405 | public instance: AxiosInstance; 406 | private securityData: SecurityDataType | null = null; 407 | private securityWorker?: ApiConfig["securityWorker"]; 408 | private secure?: boolean; 409 | private format?: ResponseType; 410 | 411 | constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig = {}) { 412 | // this.instance = axios.create({ 413 | // ...axiosConfig, 414 | // baseURL: axiosConfig.baseURL || "http://62.113.98.233:5000/api/v2", 415 | // }); 416 | this.instance = axios.create({ 417 | ...axiosConfig, 418 | baseURL: axiosConfig.baseURL || "/api/v2", 419 | }); 420 | this.secure = secure; 421 | this.format = format; 422 | this.securityWorker = securityWorker; 423 | } 424 | 425 | public setSecurityData = (data: SecurityDataType | null) => { 426 | this.securityData = data; 427 | }; 428 | 429 | protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig { 430 | const method = params1.method || (params2 && params2.method); 431 | 432 | return { 433 | ...this.instance.defaults, 434 | ...params1, 435 | ...(params2 || {}), 436 | headers: { 437 | ...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}), 438 | ...(params1.headers || {}), 439 | ...((params2 && params2.headers) || {}), 440 | }, 441 | }; 442 | } 443 | 444 | protected stringifyFormItem(formItem: unknown) { 445 | if (typeof formItem === "object" && formItem !== null) { 446 | return JSON.stringify(formItem); 447 | } else { 448 | return `${formItem}`; 449 | } 450 | } 451 | 452 | protected createFormData(input: Record): FormData { 453 | return Object.keys(input || {}).reduce((formData, key) => { 454 | const property = input[key]; 455 | const propertyContent: any[] = property instanceof Array ? property : [property]; 456 | 457 | for (const formItem of propertyContent) { 458 | const isFileType = formItem instanceof Blob || formItem instanceof File; 459 | formData.append(key, isFileType ? formItem : this.stringifyFormItem(formItem)); 460 | } 461 | 462 | return formData; 463 | }, new FormData()); 464 | } 465 | 466 | public request = async ({ 467 | secure, 468 | path, 469 | type, 470 | query, 471 | format, 472 | body, 473 | ...params 474 | }: FullRequestParams): Promise> => { 475 | const secureParams = 476 | ((typeof secure === "boolean" ? secure : this.secure) && 477 | this.securityWorker && 478 | (await this.securityWorker(this.securityData))) || 479 | {}; 480 | const requestParams = this.mergeRequestParams(params, secureParams); 481 | const responseFormat = format || this.format || undefined; 482 | 483 | if (type === ContentType.FormData && body && body !== null && typeof body === "object") { 484 | body = this.createFormData(body as Record); 485 | } 486 | 487 | return this.instance.request({ 488 | ...requestParams, 489 | headers: { 490 | ...(requestParams.headers || {}), 491 | ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), 492 | }, 493 | params: query, 494 | responseType: responseFormat, 495 | data: body, 496 | url: path, 497 | }); 498 | }; 499 | } 500 | 501 | /** 502 | * @title Swagger 503 | * @version 1.0.0 504 | * @baseUrl http://62.113.98.233:5000/api/v2 505 | * 506 | * Chats API 507 | */ 508 | export class Api extends HttpClient { 509 | auth = { 510 | /** 511 | * No description 512 | * 513 | * @tags Auth 514 | * @name SignupCreate 515 | * @summary Sign up (create user) 516 | * @request POST:/auth/signup 517 | * @response `200` `SignUpResponse` Ok 518 | * @response `400` `BadRequestError` Bad Request 519 | * @response `401` `void` Unauthorized 520 | * @response `500` `void` Unexpected error 521 | */ 522 | signupCreate: (signUpRequest: SignUpRequest, params: RequestParams = {}) => 523 | this.request({ 524 | path: `/auth/signup`, 525 | method: "POST", 526 | body: signUpRequest, 527 | type: ContentType.Json, 528 | format: "json", 529 | ...params, 530 | }), 531 | 532 | /** 533 | * No description 534 | * 535 | * @tags Auth 536 | * @name SigninCreate 537 | * @summary Sign in 538 | * @request POST:/auth/signin 539 | * @response `200` `void` Ok 540 | * @response `400` `BadRequestError` Bad Request 541 | * @response `401` `void` Unauthorized 542 | * @response `500` `void` Unexpected error 543 | */ 544 | signinCreate: (signInRequest: SignInRequest, params: RequestParams = {}) => 545 | this.request({ 546 | path: `/auth/signin`, 547 | method: "POST", 548 | body: signInRequest, 549 | type: ContentType.Json, 550 | ...params, 551 | }), 552 | 553 | /** 554 | * No description 555 | * 556 | * @tags Auth 557 | * @name UserList 558 | * @summary Get user info 559 | * @request GET:/auth/user 560 | * @response `200` `UserResponse` An array of user info 561 | * @response `400` `BadRequestError` Bad Request 562 | * @response `401` `void` Unauthorized 563 | * @response `500` `void` Unexpected error 564 | */ 565 | userList: (params: RequestParams = {}) => 566 | this.request({ 567 | path: `/auth/user`, 568 | method: "GET", 569 | format: "json", 570 | ...params, 571 | }), 572 | 573 | /** 574 | * No description 575 | * 576 | * @tags Auth 577 | * @name LogoutCreate 578 | * @summary Logout 579 | * @request POST:/auth/logout 580 | * @response `200` `void` Ok 581 | * @response `500` `void` Unexpected error 582 | */ 583 | logoutCreate: (params: RequestParams = {}) => 584 | this.request({ 585 | path: `/auth/logout`, 586 | method: "POST", 587 | ...params, 588 | }), 589 | }; 590 | chats = { 591 | /** 592 | * No description 593 | * 594 | * @tags Chats 595 | * @name ChatsList 596 | * @summary Get chats 597 | * @request GET:/chats 598 | * @response `200` `(ChatsResponse)[]` Ok 599 | * @response `401` `void` Unauthorized 600 | * @response `500` `void` Unexpected error 601 | */ 602 | chatsList: ( 603 | query?: { 604 | /** The number of items to skip before starting to collect the result set */ 605 | offset?: number; 606 | /** The numbers of items to return */ 607 | limit?: number; 608 | /** Chat's title to filter by */ 609 | title?: string; 610 | }, 611 | params: RequestParams = {}, 612 | ) => 613 | this.request({ 614 | path: `/chats`, 615 | method: "GET", 616 | query: query, 617 | format: "json", 618 | ...params, 619 | }), 620 | 621 | /** 622 | * No description 623 | * 624 | * @tags Chats 625 | * @name ChatsCreate 626 | * @summary Create chat 627 | * @request POST:/chats 628 | * @response `200` `void` Ok 629 | * @response `400` `BadRequestError` Bad Request 630 | * @response `401` `void` Unauthorized 631 | * @response `500` `void` Unexpected error 632 | */ 633 | chatsCreate: (createChatRequest: CreateChatRequest, params: RequestParams = {}) => 634 | this.request({ 635 | path: `/chats`, 636 | method: "POST", 637 | body: createChatRequest, 638 | type: ContentType.Json, 639 | ...params, 640 | }), 641 | 642 | /** 643 | * @description Delete works only for admin role. 644 | * 645 | * @tags Chats 646 | * @name ChatsDelete 647 | * @summary Delete chat by ID 648 | * @request DELETE:/chats 649 | * @response `200` `ChatDeleteResponse` Ok 650 | * @response `400` `void` Bad Request 651 | * @response `401` `void` Unauthorized 652 | * @response `403` `void` Forbidden 653 | * @response `500` `void` Unexpected error 654 | */ 655 | chatsDelete: (deleteChatRequest: ChatDeleteRequest, params: RequestParams = {}) => 656 | this.request({ 657 | path: `/chats`, 658 | method: "DELETE", 659 | body: deleteChatRequest, 660 | type: ContentType.Json, 661 | format: "json", 662 | ...params, 663 | }), 664 | 665 | /** 666 | * No description 667 | * 668 | * @tags Chats 669 | * @name ArchiveList 670 | * @summary Get archived chats 671 | * @request GET:/chats/archive 672 | * @response `200` `(ChatsResponse)[]` Ok 673 | * @response `401` `void` Unauthorized 674 | * @response `500` `void` Unexpected error 675 | */ 676 | archiveList: ( 677 | query?: { 678 | /** The number of items to skip before starting to collect the result set */ 679 | offset?: number; 680 | /** The numbers of items to return */ 681 | limit?: number; 682 | /** Chat's title to filter by */ 683 | title?: string; 684 | }, 685 | params: RequestParams = {}, 686 | ) => 687 | this.request({ 688 | path: `/chats/archive`, 689 | method: "GET", 690 | query: query, 691 | format: "json", 692 | ...params, 693 | }), 694 | 695 | /** 696 | * @description Archive chat 697 | * 698 | * @tags Chats 699 | * @name ArchiveCreate 700 | * @summary Archive chat by ID 701 | * @request POST:/chats/archive 702 | * @response `200` `ChatArchiveResponse` Ok 703 | * @response `400` `void` Bad Request 704 | * @response `401` `void` Unauthorized 705 | * @response `403` `void` Forbidden 706 | * @response `500` `void` Unexpected error 707 | */ 708 | archiveCreate: (archiveChatRequest: ChatArchiveRequest, params: RequestParams = {}) => 709 | this.request({ 710 | path: `/chats/archive`, 711 | method: "POST", 712 | body: archiveChatRequest, 713 | format: "json", 714 | ...params, 715 | }), 716 | 717 | /** 718 | * @description UnArchive chat 719 | * 720 | * @tags Chats 721 | * @name UnarchiveCreate 722 | * @summary UnArchive chat by ID 723 | * @request POST:/chats/unarchive 724 | * @response `200` `ChatArchiveResponse` Ok 725 | * @response `400` `void` Bad Request 726 | * @response `401` `void` Unauthorized 727 | * @response `403` `void` Forbidden 728 | * @response `500` `void` Unexpected error 729 | */ 730 | unarchiveCreate: (unarchiveChatRequest: ChatArchiveRequest, params: RequestParams = {}) => 731 | this.request({ 732 | path: `/chats/unarchive`, 733 | method: "POST", 734 | body: unarchiveChatRequest, 735 | format: "json", 736 | ...params, 737 | }), 738 | 739 | /** 740 | * No description 741 | * 742 | * @tags Chats 743 | * @name CommonDetail 744 | * @summary Get common chat with current chat user (only works for two users chats) 745 | * @request GET:/chats/{id}/common 746 | * @response `200` `(ChatsResponse)[]` Ok 747 | * @response `400` `void` BadRequest 748 | * @response `401` `void` Unauthorized 749 | * @response `404` `void` Not found chat 750 | * @response `500` `void` Unexpected error 751 | */ 752 | commonDetail: (id: number, params: RequestParams = {}) => 753 | this.request({ 754 | path: `/chats/${id}/common`, 755 | method: "GET", 756 | format: "json", 757 | ...params, 758 | }), 759 | 760 | /** 761 | * No description 762 | * 763 | * @tags Chats 764 | * @name UsersDetail 765 | * @summary Get chat users 766 | * @request GET:/chats/{id}/users 767 | * @response `200` `(ChatUserResponse)[]` Ok 768 | * @response `401` `void` Unauthorized 769 | * @response `404` `void` Not found chat 770 | * @response `500` `void` Unexpected error 771 | */ 772 | usersDetail: ( 773 | id: number, 774 | query?: { 775 | /** The number of items to skip before starting to collect the result set */ 776 | offset?: number; 777 | /** The numbers of items to return */ 778 | limit?: number; 779 | /** User's '{first_name} {second_name}' to filter */ 780 | name?: string; 781 | /** User's email to filter */ 782 | email?: string; 783 | }, 784 | params: RequestParams = {}, 785 | ) => 786 | this.request({ 787 | path: `/chats/${id}/users`, 788 | method: "GET", 789 | query: query, 790 | format: "json", 791 | ...params, 792 | }), 793 | 794 | /** 795 | * No description 796 | * 797 | * @tags Chats 798 | * @name GetChats 799 | * @summary Get new messages count 800 | * @request GET:/chats/new/{id} 801 | * @response `200` `UnreadCountResponse` Ok 802 | * @response `401` `void` Unauthorized 803 | * @response `500` `void` Unexpected error 804 | */ 805 | getChats: (id: number, params: RequestParams = {}) => 806 | this.request({ 807 | path: `/chats/new/${id}`, 808 | method: "GET", 809 | format: "json", 810 | ...params, 811 | }), 812 | 813 | /** 814 | * No description 815 | * 816 | * @tags Chats 817 | * @name UsersUpdate 818 | * @summary Add users to chat 819 | * @request PUT:/chats/users 820 | * @response `200` `void` Ok 821 | * @response `400` `BadRequestError` Bad Request 822 | * @response `401` `void` Unauthorized 823 | * @response `500` `void` Unexpected error 824 | */ 825 | usersUpdate: (usersRequest: UsersRequest, params: RequestParams = {}) => 826 | this.request({ 827 | path: `/chats/users`, 828 | method: "PUT", 829 | body: usersRequest, 830 | ...params, 831 | }), 832 | 833 | /** 834 | * No description 835 | * 836 | * @tags Chats 837 | * @name UsersDelete 838 | * @summary Delete users from chat 839 | * @request DELETE:/chats/users 840 | * @response `200` `void` Ok 841 | * @response `400` `BadRequestError` Bad Request 842 | * @response `401` `void` Unauthorized 843 | * @response `500` `void` Unexpected error 844 | */ 845 | usersDelete: (usersRequest: UsersRequest, params: RequestParams = {}) => 846 | this.request({ 847 | path: `/chats/users`, 848 | method: "DELETE", 849 | body: usersRequest, 850 | ...params, 851 | }), 852 | 853 | /** 854 | * @description Request token to connect to messages server 855 | * 856 | * @tags Chats 857 | * @name TokenCreate 858 | * @summary Get chat users 859 | * @request POST:/chats/token/{id} 860 | * @response `200` `(ChatsMessagesTokenResponse)[]` Ok 861 | * @response `401` `void` Unauthorized 862 | * @response `500` `void` Unexpected error 863 | */ 864 | tokenCreate: (id: number, params: RequestParams = {}) => 865 | this.request({ 866 | path: `/chats/token/${id}`, 867 | method: "POST", 868 | format: "json", 869 | ...params, 870 | }), 871 | }; 872 | oauth = { 873 | /** 874 | * No description 875 | * 876 | * @tags Oauth 877 | * @name YandexCreate 878 | * @summary Sign in / sign up with yandex 879 | * @request POST:/oauth/yandex 880 | * @response `200` `void` Ok 881 | * @response `400` `BadRequestError` Bad Request (No such redirect_uri or wrong code) 882 | * @response `401` `void` Unauthorized 883 | * @response `500` `void` Unexpected error 884 | */ 885 | yandexCreate: (OauthSignInRequest: OauthSignInRequest, params: RequestParams = {}) => 886 | this.request({ 887 | path: `/oauth/yandex`, 888 | method: "POST", 889 | body: OauthSignInRequest, 890 | type: ContentType.Json, 891 | ...params, 892 | }), 893 | 894 | /** 895 | * No description 896 | * 897 | * @tags Oauth 898 | * @name YandexServiceIdList 899 | * @summary Get service id 900 | * @request GET:/oauth/yandex/service-id 901 | * @response `200` `ServiceId` Yandex client id 902 | * @response `400` `BadRequestError` Bad Request (No such redirect_uri refistered) 903 | * @response `500` `void` Unexpected error 904 | */ 905 | yandexServiceIdList: ( 906 | query?: { 907 | /** Redirect uri that you are using for oauth */ 908 | redirect_uri?: string; 909 | }, 910 | params: RequestParams = {}, 911 | ) => 912 | this.request({ 913 | path: `/oauth/yandex/service-id`, 914 | method: "GET", 915 | query: query, 916 | format: "json", 917 | ...params, 918 | }), 919 | }; 920 | user = { 921 | /** 922 | * No description 923 | * 924 | * @tags Users 925 | * @name ProfileUpdate 926 | * @summary Change user profile 927 | * @request PUT:/user/profile 928 | * @response `200` `UserResponse` Ok 929 | * @response `400` `BadRequestError` Bad Request 930 | * @response `401` `void` Unauthorized 931 | * @response `500` `void` Unexpected error 932 | */ 933 | profileUpdate: (userRequest: UserUpdateRequest, params: RequestParams = {}) => 934 | this.request({ 935 | path: `/user/profile`, 936 | method: "PUT", 937 | body: userRequest, 938 | type: ContentType.Json, 939 | format: "json", 940 | ...params, 941 | }), 942 | 943 | /** 944 | * No description 945 | * 946 | * @tags Users 947 | * @name ProfileAvatarUpdate 948 | * @summary Change user avatar 949 | * @request PUT:/user/profile/avatar 950 | * @response `200` `UserResponse` Ok 951 | * @response `400` `BadRequestError` Bad Request 952 | * @response `401` `void` Unauthorized 953 | * @response `500` `void` Unexpected error 954 | */ 955 | profileAvatarUpdate: ( 956 | data: { 957 | /** 958 | * Avatar 959 | * @format binary 960 | */ 961 | avatar: File; 962 | }, 963 | params: RequestParams = {}, 964 | ) => 965 | this.request({ 966 | path: `/user/profile/avatar`, 967 | method: "PUT", 968 | body: data, 969 | type: ContentType.FormData, 970 | format: "json", 971 | ...params, 972 | }), 973 | 974 | /** 975 | * No description 976 | * 977 | * @tags Users 978 | * @name PasswordUpdate 979 | * @summary Change user password 980 | * @request PUT:/user/password 981 | * @response `200` `void` Ok 982 | * @response `400` `BadRequestError` Bad Request 983 | * @response `401` `void` Unauthorized 984 | * @response `500` `void` Unexpected error 985 | */ 986 | passwordUpdate: (changePasswordRequest: ChangePasswordRequest, params: RequestParams = {}) => 987 | this.request({ 988 | path: `/user/password`, 989 | method: "PUT", 990 | body: changePasswordRequest, 991 | type: ContentType.Json, 992 | ...params, 993 | }), 994 | 995 | /** 996 | * No description 997 | * 998 | * @tags Users 999 | * @name UserDetail 1000 | * @summary Get user by id 1001 | * @request GET:/user/{id} 1002 | * @response `200` `UserResponse` Ok 1003 | * @response `401` `void` Unauthorized 1004 | * @response `500` `void` Unexpected error 1005 | */ 1006 | userDetail: (id: number, params: RequestParams = {}) => 1007 | this.request({ 1008 | path: `/user/${id}`, 1009 | method: "GET", 1010 | format: "json", 1011 | ...params, 1012 | }), 1013 | 1014 | /** 1015 | * No description 1016 | * 1017 | * @tags Users 1018 | * @name SearchCreate 1019 | * @summary Search for user by login (max 10) 1020 | * @request POST:/user/search 1021 | * @response `200` `(UserResponse)[]` Ok 1022 | * @response `400` `BadRequestError` Bad Request 1023 | * @response `401` `void` Unauthorized 1024 | * @response `500` `void` Unexpected error 1025 | */ 1026 | searchCreate: (findUserRequest: FindUserRequest, params: RequestParams = {}) => 1027 | this.request({ 1028 | path: `/user/search`, 1029 | method: "POST", 1030 | body: findUserRequest, 1031 | type: ContentType.Json, 1032 | format: "json", 1033 | ...params, 1034 | }), 1035 | }; 1036 | } 1037 | -------------------------------------------------------------------------------- /src/api/base/swagger.yaml: -------------------------------------------------------------------------------- 1 | info: 2 | description: Chats API 3 | title: Swagger 4 | version: 1.0.0 5 | host: 62.113.98.233:5000 6 | basePath: /api/v2 7 | produces: 8 | - application/json 9 | - application/xml 10 | schemes: 11 | - http 12 | swagger: '2.0' 13 | paths: 14 | /auth/signup: 15 | post: 16 | parameters: 17 | - name: signUpRequest 18 | in: body 19 | description: User data 20 | required: true 21 | schema: 22 | $ref: '#/definitions/SignUpRequest' 23 | description: '' 24 | tags: 25 | - Auth 26 | responses: 27 | '200': 28 | description: Ok 29 | schema: 30 | $ref: '#/definitions/SignUpResponse' 31 | '400': 32 | description: Bad Request 33 | schema: 34 | $ref: '#/definitions/BadRequestError' 35 | '401': 36 | description: Unauthorized 37 | '500': 38 | description: Unexpected error 39 | summary: Sign up (create user) 40 | /auth/signin: 41 | post: 42 | parameters: 43 | - name: signInRequest 44 | in: body 45 | description: User data 46 | required: true 47 | schema: 48 | $ref: '#/definitions/SignInRequest' 49 | description: '' 50 | tags: 51 | - Auth 52 | responses: 53 | '200': 54 | description: Ok 55 | '400': 56 | description: Bad Request 57 | schema: 58 | $ref: '#/definitions/BadRequestError' 59 | '401': 60 | description: Unauthorized 61 | '500': 62 | description: Unexpected error 63 | summary: Sign in 64 | /auth/user: 65 | get: 66 | parameters: [] 67 | description: '' 68 | tags: 69 | - Auth 70 | responses: 71 | '200': 72 | description: An array of user info 73 | schema: 74 | $ref: '#/definitions/UserResponse' 75 | '400': 76 | description: Bad Request 77 | schema: 78 | $ref: '#/definitions/BadRequestError' 79 | '401': 80 | description: Unauthorized 81 | '500': 82 | description: Unexpected error 83 | summary: Get user info 84 | /auth/logout: 85 | post: 86 | parameters: [] 87 | description: '' 88 | tags: 89 | - Auth 90 | responses: 91 | '200': 92 | description: Ok 93 | '500': 94 | description: Unexpected error 95 | summary: Logout 96 | 97 | /chats: 98 | get: 99 | parameters: 100 | - in: query 101 | name: offset 102 | schema: 103 | type: integer 104 | description: The number of items to skip before starting to collect the result set 105 | - in: query 106 | name: limit 107 | schema: 108 | type: integer 109 | description: The numbers of items to return 110 | - in: query 111 | name: title 112 | schema: 113 | type: string 114 | description: Chat's title to filter by 115 | description: '' 116 | tags: 117 | - Chats 118 | responses: 119 | '200': 120 | description: Ok 121 | schema: 122 | type: array 123 | items: 124 | $ref: '#/definitions/ChatsResponse' 125 | '401': 126 | description: Unauthorized 127 | '500': 128 | description: Unexpected error 129 | summary: Get chats 130 | post: 131 | parameters: 132 | - name: createChatRequest 133 | in: body 134 | description: Chat data 135 | required: true 136 | schema: 137 | $ref: '#/definitions/CreateChatRequest' 138 | description: '' 139 | tags: 140 | - Chats 141 | responses: 142 | '200': 143 | description: Ok 144 | '400': 145 | description: Bad Request 146 | schema: 147 | $ref: '#/definitions/BadRequestError' 148 | '401': 149 | description: Unauthorized 150 | '500': 151 | description: Unexpected error 152 | summary: Create chat 153 | delete: 154 | parameters: 155 | - name: deleteChatRequest 156 | in: body 157 | description: '' 158 | required: true 159 | schema: 160 | $ref: '#/definitions/ChatDeleteRequest' 161 | description: 'Delete works only for admin role.' 162 | tags: 163 | - Chats 164 | responses: 165 | '200': 166 | description: Ok 167 | schema: 168 | $ref: '#/definitions/ChatDeleteResponse' 169 | '400': 170 | description: Bad Request 171 | '401': 172 | description: Unauthorized 173 | '403': 174 | description: Forbidden 175 | '500': 176 | description: Unexpected error 177 | summary: Delete chat by ID 178 | 179 | /chats/archive: 180 | get: 181 | parameters: 182 | - in: query 183 | name: offset 184 | schema: 185 | type: integer 186 | description: The number of items to skip before starting to collect the result set 187 | - in: query 188 | name: limit 189 | schema: 190 | type: integer 191 | description: The numbers of items to return 192 | - in: query 193 | name: title 194 | schema: 195 | type: string 196 | description: Chat's title to filter by 197 | description: '' 198 | tags: 199 | - Chats 200 | responses: 201 | '200': 202 | description: Ok 203 | schema: 204 | type: array 205 | items: 206 | $ref: '#/definitions/ChatsResponse' 207 | '401': 208 | description: Unauthorized 209 | '500': 210 | description: Unexpected error 211 | summary: Get archived chats 212 | post: 213 | parameters: 214 | - name: archiveChatRequest 215 | in: body 216 | description: '' 217 | required: true 218 | schema: 219 | $ref: '#/definitions/ChatArchiveRequest' 220 | description: 'Archive chat' 221 | tags: 222 | - Chats 223 | responses: 224 | '200': 225 | description: Ok 226 | schema: 227 | $ref: '#/definitions/ChatArchiveResponse' 228 | '400': 229 | description: Bad Request 230 | '401': 231 | description: Unauthorized 232 | '403': 233 | description: Forbidden 234 | '500': 235 | description: Unexpected error 236 | summary: Archive chat by ID 237 | 238 | /chats/unarchive: 239 | post: 240 | parameters: 241 | - name: unarchiveChatRequest 242 | in: body 243 | description: '' 244 | required: true 245 | schema: 246 | $ref: '#/definitions/ChatArchiveRequest' 247 | description: 'UnArchive chat' 248 | tags: 249 | - Chats 250 | responses: 251 | '200': 252 | description: Ok 253 | schema: 254 | $ref: '#/definitions/ChatArchiveResponse' 255 | '400': 256 | description: Bad Request 257 | '401': 258 | description: Unauthorized 259 | '403': 260 | description: Forbidden 261 | '500': 262 | description: Unexpected error 263 | summary: UnArchive chat by ID 264 | 265 | /chats/{id}/common: 266 | get: 267 | parameters: 268 | - in: path 269 | name: id 270 | schema: 271 | type: integer 272 | required: true 273 | description: Numeric chat id 274 | description: '' 275 | tags: 276 | - Chats 277 | responses: 278 | '200': 279 | description: Ok 280 | schema: 281 | type: array 282 | items: 283 | $ref: '#/definitions/ChatsResponse' 284 | '400': 285 | description: BadRequest 286 | '401': 287 | description: Unauthorized 288 | '404': 289 | description: Not found chat 290 | '500': 291 | description: Unexpected error 292 | summary: Get common chat with current chat user (only works for two users chats) 293 | 294 | /chats/{id}/users: 295 | get: 296 | parameters: 297 | - in: path 298 | name: id 299 | schema: 300 | type: integer 301 | required: true 302 | description: Numeric chat id 303 | - in: query 304 | name: offset 305 | schema: 306 | type: integer 307 | description: The number of items to skip before starting to collect the result set 308 | - in: query 309 | name: limit 310 | schema: 311 | type: integer 312 | description: The numbers of items to return 313 | - in: query 314 | name: name 315 | schema: 316 | type: string 317 | description: User's '{first_name} {second_name}' to filter 318 | - in: query 319 | name: email 320 | schema: 321 | type: string 322 | description: User's email to filter 323 | description: '' 324 | tags: 325 | - Chats 326 | responses: 327 | '200': 328 | description: Ok 329 | schema: 330 | type: array 331 | items: 332 | $ref: '#/definitions/ChatUserResponse' 333 | '401': 334 | description: Unauthorized 335 | '404': 336 | description: Not found chat 337 | '500': 338 | description: Unexpected error 339 | summary: Get chat users 340 | 341 | /chats/new/{id}: 342 | get: 343 | parameters: 344 | - in: path 345 | name: id 346 | schema: 347 | type: integer 348 | required: true 349 | description: Numeric chat id 350 | description: '' 351 | tags: 352 | - Chats 353 | responses: 354 | '200': 355 | description: Ok 356 | schema: 357 | $ref: '#/definitions/UnreadCountResponse' 358 | '401': 359 | description: Unauthorized 360 | '500': 361 | description: Unexpected error 362 | summary: Get new messages count 363 | /chats/users: 364 | put: 365 | parameters: 366 | - name: usersRequest 367 | in: body 368 | description: '' 369 | required: true 370 | schema: 371 | $ref: '#/definitions/UsersRequest' 372 | description: '' 373 | tags: 374 | - Chats 375 | responses: 376 | '200': 377 | description: Ok 378 | '400': 379 | description: Bad Request 380 | schema: 381 | $ref: '#/definitions/BadRequestError' 382 | '401': 383 | description: Unauthorized 384 | '500': 385 | description: Unexpected error 386 | summary: Add users to chat 387 | delete: 388 | parameters: 389 | - name: usersRequest 390 | in: body 391 | description: '' 392 | required: true 393 | schema: 394 | $ref: '#/definitions/UsersRequest' 395 | description: '' 396 | tags: 397 | - Chats 398 | responses: 399 | '200': 400 | description: Ok 401 | '400': 402 | description: Bad Request 403 | schema: 404 | $ref: '#/definitions/BadRequestError' 405 | '401': 406 | description: Unauthorized 407 | '500': 408 | description: Unexpected error 409 | summary: Delete users from chat 410 | 411 | /chats/token/{id}: 412 | post: 413 | parameters: 414 | - in: path 415 | name: id 416 | schema: 417 | type: integer 418 | required: true 419 | description: Numeric chat id 420 | description: 'Request token to connect to messages server' 421 | tags: 422 | - Chats 423 | responses: 424 | '200': 425 | description: Ok 426 | schema: 427 | type: array 428 | items: 429 | $ref: '#/definitions/ChatsMessagesTokenResponse' 430 | '401': 431 | description: Unauthorized 432 | '500': 433 | description: Unexpected error 434 | summary: Get chat users 435 | /oauth/yandex: 436 | post: 437 | parameters: 438 | - name: OauthSignInRequest 439 | in: body 440 | description: Oauth data 441 | required: true 442 | schema: 443 | $ref: '#/definitions/OauthSignInRequest' 444 | description: '' 445 | tags: 446 | - Oauth 447 | responses: 448 | '200': 449 | description: Ok 450 | '400': 451 | description: Bad Request (No such redirect_uri or wrong code) 452 | schema: 453 | $ref: '#/definitions/BadRequestError' 454 | '401': 455 | description: Unauthorized 456 | '500': 457 | description: Unexpected error 458 | summary: Sign in / sign up with yandex 459 | /oauth/yandex/service-id: 460 | get: 461 | parameters: 462 | - in: query 463 | name: redirect_uri 464 | schema: 465 | type: string 466 | description: Redirect uri that you are using for oauth 467 | description: '' 468 | tags: 469 | - Oauth 470 | responses: 471 | '200': 472 | description: Yandex client id 473 | schema: 474 | $ref: '#/definitions/ServiceId' 475 | '400': 476 | description: Bad Request (No such redirect_uri refistered) 477 | schema: 478 | $ref: '#/definitions/BadRequestError' 479 | '500': 480 | description: Unexpected error 481 | summary: Get service id 482 | /user/profile: 483 | put: 484 | parameters: 485 | - name: userRequest 486 | in: body 487 | description: User data 488 | required: true 489 | schema: 490 | $ref: '#/definitions/UserUpdateRequest' 491 | description: '' 492 | tags: 493 | - Users 494 | responses: 495 | '200': 496 | description: Ok 497 | schema: 498 | $ref: '#/definitions/UserResponse' 499 | '400': 500 | description: Bad Request 501 | schema: 502 | $ref: '#/definitions/BadRequestError' 503 | '401': 504 | description: Unauthorized 505 | '500': 506 | description: Unexpected error 507 | summary: Change user profile 508 | /user/profile/avatar: 509 | put: 510 | parameters: 511 | - name: avatar 512 | in: formData 513 | description: Avatar 514 | required: true 515 | type: file 516 | consumes: ['multipart/form-data'] 517 | description: '' 518 | tags: 519 | - Users 520 | responses: 521 | '200': 522 | description: Ok 523 | schema: 524 | $ref: '#/definitions/UserResponse' 525 | '400': 526 | description: Bad Request 527 | schema: 528 | $ref: '#/definitions/BadRequestError' 529 | '401': 530 | description: Unauthorized 531 | '500': 532 | description: Unexpected error 533 | summary: Change user avatar 534 | /user/password: 535 | put: 536 | parameters: 537 | - name: changePasswordRequest 538 | in: body 539 | description: Password request 540 | required: true 541 | schema: 542 | $ref: '#/definitions/ChangePasswordRequest' 543 | description: '' 544 | tags: 545 | - Users 546 | responses: 547 | '200': 548 | description: Ok 549 | '400': 550 | description: Bad Request 551 | schema: 552 | $ref: '#/definitions/BadRequestError' 553 | '401': 554 | description: Unauthorized 555 | '500': 556 | description: Unexpected error 557 | summary: Change user password 558 | /user/{id}: 559 | get: 560 | parameters: 561 | - in: path 562 | name: id 563 | schema: 564 | type: integer 565 | required: true 566 | description: Numeric user id 567 | description: '' 568 | tags: 569 | - Users 570 | responses: 571 | '200': 572 | description: Ok 573 | schema: 574 | $ref: '#/definitions/UserResponse' 575 | '401': 576 | description: Unauthorized 577 | '500': 578 | description: Unexpected error 579 | summary: Get user by id 580 | /user/search: 581 | post: 582 | parameters: 583 | - name: findUserRequest 584 | in: body 585 | description: User data 586 | required: true 587 | schema: 588 | $ref: '#/definitions/FindUserRequest' 589 | description: '' 590 | tags: 591 | - Users 592 | responses: 593 | '200': 594 | description: Ok 595 | schema: 596 | type: array 597 | items: 598 | $ref: '#/definitions/UserResponse' 599 | '400': 600 | description: Bad Request 601 | schema: 602 | $ref: '#/definitions/BadRequestError' 603 | '401': 604 | description: Unauthorized 605 | '500': 606 | description: Unexpected error 607 | summary: Search for user by login (max 10) 608 | 609 | definitions: 610 | UserResponse: 611 | example: 612 | id: 123 613 | first_name: Petya 614 | second_name: Pupkin 615 | display_name: Petya Pupkin 616 | login: userLogin 617 | email: my@email.com 618 | phone: "89223332211" 619 | avatar: /path/to/avatar.jpg 620 | required: 621 | - id 622 | - first_name 623 | - second_name 624 | - display_name 625 | - login 626 | - email 627 | - phone 628 | - avatar 629 | properties: 630 | id: 631 | type: integer 632 | description: User id 633 | first_name: 634 | type: string 635 | description: First name 636 | second_name: 637 | type: string 638 | description: Second name 639 | display_name: 640 | type: string 641 | description: Display name 642 | login: 643 | type: string 644 | description: User login - unique 645 | email: 646 | type: string 647 | description: Email 648 | phone: 649 | type: string 650 | description: Phone 651 | avatar: 652 | type: string 653 | description: Avatar 654 | SignUpRequest: 655 | required: 656 | - first_name 657 | - second_name 658 | - login 659 | - email 660 | - password 661 | - phone 662 | properties: 663 | first_name: 664 | type: string 665 | description: First name 666 | second_name: 667 | type: string 668 | description: Second name 669 | login: 670 | type: string 671 | description: User login - unique 672 | email: 673 | type: string 674 | description: Email /^\S+@\S+$/ 675 | password: 676 | type: string 677 | description: Password 678 | phone: 679 | type: string 680 | description: 'Phone /^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$/' 681 | SignInRequest: 682 | required: 683 | - login 684 | - password 685 | properties: 686 | login: 687 | type: string 688 | description: User login 689 | password: 690 | type: string 691 | description: Password 692 | SignUpResponse: 693 | required: 694 | - id 695 | properties: 696 | id: 697 | type: number 698 | description: Created User ID 699 | CreateChatRequest: 700 | required: 701 | - title 702 | properties: 703 | title: 704 | type: string 705 | description: Chat title 706 | UsersRequest: 707 | required: 708 | - users 709 | - chatId 710 | properties: 711 | users: 712 | type: array 713 | items: 714 | type: integer 715 | chatId: 716 | type: integer 717 | description: Chat id 718 | ChatsResponse: 719 | example: 720 | id: 123 721 | title: my-chat 722 | avatar: /123/avatar1.jpg 723 | unread_count: 15 724 | last_message: 725 | user: 726 | first_name: Petya 727 | second_name: Pupkin 728 | avatar: /path/to/avatar.jpg 729 | email: my@email.com 730 | login: userLogin 731 | phone: 8(911)-222-33-22 732 | time: 2020-01-02 14:22:22Z 733 | content: this is message content 734 | required: 735 | - id 736 | - title 737 | - avatar 738 | - unread_count 739 | - last_message 740 | properties: 741 | id: 742 | type: integer 743 | description: Chat id 744 | title: 745 | type: string 746 | description: Chat title 747 | avatar: 748 | type: string 749 | description: Chat avatar 750 | unread_count: 751 | type: integer 752 | description: Number of unread messages in chat for current user 753 | last_message: 754 | type: object 755 | properties: 756 | user: 757 | type: object 758 | $ref: '#/definitions/UserResponse' 759 | format: date-time 760 | description: Message user (sender) 761 | time: 762 | type: string 763 | format: timestamp 764 | description: Message timestamp 765 | content: 766 | type: string 767 | description: Message content 768 | 769 | ChatDeleteRequest: 770 | required: 771 | - chatId 772 | properties: 773 | chatId: 774 | type: integer 775 | description: Chat id 776 | ChatDeleteResponse: 777 | example: 778 | userId: 12 779 | result: 780 | id: 123 781 | title: deleted-chat 782 | avatar: /123/avatar1.jpg 783 | required: 784 | - userId 785 | - result 786 | properties: 787 | userId: 788 | type: integer 789 | description: User id 790 | result: 791 | type: object 792 | $ref: '#/definitions/ChatsResponse' 793 | ChatArchiveRequest: 794 | required: 795 | - chatId 796 | properties: 797 | chatId: 798 | type: integer 799 | description: Chat id 800 | ChatArchiveResponse: 801 | required: 802 | - userId 803 | - result 804 | properties: 805 | userId: 806 | type: integer 807 | description: User id 808 | result: 809 | type: object 810 | $ref: '#/definitions/ChatsResponse' 811 | ChatsMessagesTokenResponse: 812 | required: 813 | - token 814 | properties: 815 | token: 816 | type: string 817 | description: Token for web socket server 818 | UnreadCountResponse: 819 | example: 820 | unread_count: 12 821 | required: 822 | - unread_count 823 | properties: 824 | unread_count: 825 | type: integer 826 | description: New messages count 827 | LeaderboardNewLeaderRequest: 828 | required: 829 | - data 830 | - ratingFieldName 831 | properties: 832 | data: 833 | type: object 834 | description: 'Leaderboard data object, any type' 835 | ratingFieldName: 836 | type: string 837 | description: >- 838 | Which field is used to sort (if new value of the field more than old, 839 | data is stored) 840 | teamName: 841 | type: string 842 | description: >- 843 | Your team name. Used to make unique leaderboard for each project. 844 | LeaderboardRequest: 845 | required: 846 | - ratingFieldName 847 | - cursor 848 | - limit 849 | properties: 850 | ratingFieldName: 851 | type: string 852 | description: Which field is used to sort 853 | cursor: 854 | type: integer 855 | description: Used to paginate between pages. If limit is 10, then for the 1st page - cursor=0, for the 2nd page - cursor=10. 856 | limit: 857 | type: integer 858 | description: Maximum amount of leaders to return 859 | OauthSignInRequest: 860 | required: 861 | - code 862 | - redirect_uri 863 | properties: 864 | code: 865 | type: string 866 | description: User code from Yandex 867 | redirect_uri: 868 | type: string 869 | description: Redirect uri that you are using for oauth 870 | ServiceId: 871 | required: 872 | - service_id 873 | properties: 874 | service_id: 875 | type: string 876 | description: Service id 877 | BadRequestError: 878 | required: 879 | - reason 880 | properties: 881 | reason: 882 | type: string 883 | description: Error message 884 | UserUpdateRequest: 885 | required: 886 | - first_name 887 | - second_name 888 | - display_name 889 | - phone 890 | - login 891 | - email 892 | properties: 893 | first_name: 894 | type: string 895 | description: First name 896 | second_name: 897 | type: string 898 | description: Second name 899 | display_name: 900 | type: string 901 | description: Display Name 902 | login: 903 | type: string 904 | description: User login - unique 905 | email: 906 | type: string 907 | description: Email 908 | phone: 909 | type: string 910 | description: Phone 911 | UserRequest: 912 | required: 913 | - first_name 914 | - second_name 915 | - display_name 916 | - phone 917 | - login 918 | - email 919 | properties: 920 | first_name: 921 | type: string 922 | description: First name 923 | second_name: 924 | type: string 925 | description: Second name 926 | display_name: 927 | type: string 928 | description: Display Name 929 | login: 930 | type: string 931 | description: User login - unique 932 | email: 933 | type: string 934 | description: Email 935 | phone: 936 | type: string 937 | description: Phone 938 | FindUserRequest: 939 | required: 940 | - login 941 | properties: 942 | login: 943 | type: string 944 | description: User login (beginning of login) 945 | ChangePasswordRequest: 946 | required: 947 | - oldPassword 948 | - newPassword 949 | properties: 950 | oldPassword: 951 | type: string 952 | description: Old password 953 | newPassword: 954 | type: string 955 | description: New password 956 | Resource: 957 | example: 958 | id: 123 959 | user_id: 231 960 | path: /32543654dsf/434534r3rsddfs_my-file.jpg 961 | filename: my-file.jpg 962 | content_type: image/jpeg 963 | content_size: 543672 964 | upload_date: 2020-01-02 14:22:22Z 965 | required: 966 | - id 967 | - user_id 968 | - path 969 | - filename 970 | - content_type 971 | - content_size 972 | - upload_date 973 | properties: 974 | id: 975 | type: integer 976 | description: Message id 977 | user_id: 978 | type: integer 979 | description: User id 980 | path: 981 | type: string 982 | description: Server relative file path 983 | filename: 984 | type: string 985 | description: Initial file name 986 | content_type: 987 | type: string 988 | description: File content type (e.g "image/jpeg" for .jpg images) 989 | content_size: 990 | type: integer 991 | description: File size in bytes 992 | upload_date: 993 | type: string 994 | format: date-time 995 | description: Resource uploading time 996 | 997 | ChatMessage: 998 | example: 999 | id: 123 1000 | user_id: 231 1001 | chat_id: 312 1002 | time: 2020-01-02 14:22:22Z 1003 | type: file 1004 | content: 132 1005 | file: 1006 | id: 132 1007 | user_id: 231 1008 | path: /32543654dsf/434534r3rsddfs_my-file.jpg 1009 | filename: my-file.jpg 1010 | content_type: image/jpeg 1011 | content_size: 543672 1012 | upload_date: 2020-01-02 14:22:22Z 1013 | required: 1014 | - id 1015 | - user_id 1016 | - chat_id 1017 | - time 1018 | - type 1019 | - content 1020 | properties: 1021 | id: 1022 | type: integer 1023 | description: Message id 1024 | user_id: 1025 | type: integer 1026 | description: User id 1027 | chat_id: 1028 | type: integer 1029 | description: Chat id 1030 | time: 1031 | type: string 1032 | format: date-time 1033 | description: Message sent time 1034 | type: 1035 | type: string 1036 | enum: [ message, file ] 1037 | description: Message type 1038 | content: 1039 | type: string 1040 | description: Message content (message text for messages and resourceId for files) 1041 | file: 1042 | $ref: '#/definitions/Resource' 1043 | description: File 1044 | ChatUserResponse: 1045 | example: 1046 | id: 123 1047 | first_name: petya 1048 | second_name: petrov 1049 | display_name: petya petrov 1050 | login: my-login 1051 | email: my@email.com 1052 | phone: "89223332211" 1053 | avatar: /path/to/my-file.jpg 1054 | role: admin 1055 | required: 1056 | - id 1057 | - first_name 1058 | - second_name 1059 | - display_name 1060 | - login 1061 | - email 1062 | - phone 1063 | - avatar 1064 | - role 1065 | properties: 1066 | id: 1067 | type: integer 1068 | description: User id 1069 | first_name: 1070 | type: string 1071 | description: First name 1072 | second_name: 1073 | type: string 1074 | description: Second name 1075 | display_name: 1076 | type: string 1077 | description: Display name 1078 | login: 1079 | type: string 1080 | description: User login - unique 1081 | email: 1082 | type: string 1083 | description: Email 1084 | phone: 1085 | type: string 1086 | description: Phone 1087 | avatar: 1088 | type: string 1089 | description: Avatar 1090 | role: 1091 | type: string 1092 | enum: [admin, regular] 1093 | description: User role 1094 | StaticChartRequest: 1095 | required: 1096 | - chartSize 1097 | properties: 1098 | chartSize: 1099 | type: string 1100 | description: Number of points in chart (10 / 100 / 1000) 1101 | enum: [ small, medium, large ] 1102 | LiveChartRequest: 1103 | required: 1104 | - next 1105 | properties: 1106 | next: 1107 | type: number 1108 | format: integer 1109 | default: 0 1110 | description: Works as a cursor (initial value should be zero, all the next values are taken from the backend response) 1111 | ChartSchema: 1112 | type: array 1113 | items: 1114 | type: object 1115 | properties: 1116 | x: 1117 | type: string 1118 | format: date-time 1119 | description: X axis (datetime) 1120 | y1: 1121 | type: number 1122 | format: float 1123 | y2: 1124 | type: number 1125 | format: float 1126 | StaticChartResponse: 1127 | properties: 1128 | data: 1129 | $ref: '#/definitions/ChartSchema' 1130 | description: Chart points 1131 | LiveChartResponse: 1132 | properties: 1133 | next: 1134 | type: integer 1135 | example: 5 1136 | description: Used as a cursor (pass this value to the next request) 1137 | data: 1138 | $ref: '#/definitions/ChartSchema' 1139 | description: Chart points 1140 | LiveVideoInfoRequest: 1141 | required: 1142 | - iteration 1143 | properties: 1144 | iteration: 1145 | type: number 1146 | format: integer 1147 | default: 0 1148 | description: Works as a cursor (iterate + 1 each request) 1149 | VideoInfoResponse: 1150 | required: 1151 | - size 1152 | properties: 1153 | size: 1154 | type: number 1155 | format: integer 1156 | example: 4096 1157 | description: Video size in bytes 1158 | Sticker: 1159 | type: object 1160 | properties: 1161 | id: 1162 | type: number 1163 | format: integer 1164 | example: 123 1165 | description: Sticker id (send to chat with WS) 1166 | path: 1167 | type: string 1168 | example: /stickers/2346-dfsg-425-sdfs/14534.gif 1169 | description: Url for sticker resource(image) 1170 | StickerPack: 1171 | type: object 1172 | properties: 1173 | title: 1174 | type: string 1175 | example: pack-title 1176 | description: Sticker pack title 1177 | user_id: 1178 | type: number 1179 | format: integer 1180 | example: 123 1181 | description: User id that created this pack 1182 | stickers: 1183 | type: array 1184 | items: 1185 | type: string 1186 | example: /stickers/2346-dfsg-425-sdfs/14534.gif 1187 | description: Url for sticker resource(image) 1188 | StickerPacksResponse: 1189 | properties: 1190 | data: 1191 | type: array 1192 | items: 1193 | $ref: '#/definitions/StickerPack' 1194 | description: StickerPacks 1195 | StickersResponse: 1196 | properties: 1197 | data: 1198 | type: array 1199 | items: 1200 | $ref: '#/definitions/Sticker' 1201 | description: Stickers 1202 | responses: {} 1203 | parameters: {} 1204 | securityDefinitions: {} 1205 | tags: 1206 | - name: Auth 1207 | description: Auth operations 1208 | - name: Chats 1209 | description: Chats operations 1210 | - name: Leaderboard 1211 | description: Leaderboard operations (prefix /game/api/v2) 1212 | - name: Oauth 1213 | description: Oauth service 1214 | - name: Users 1215 | description: Users operations 1216 | - name: Charts 1217 | description: Charts operations 1218 | - name: Videos 1219 | description: Videos operations 1220 | - name: Stickers 1221 | description: Stickers operations 1222 | --------------------------------------------------------------------------------