• Как найти и вернуть записи в Mongodb?

    nowm
    @nowm
    Вот это должно быть параметром метода find: { "title": "статьи","text": "статьи"}. Но у вас в качестве критерия для запроса указан пустой объект (это значит, что выберутся все документы из коллекции), а в select указан дополнительный объект, который говорит, что нужно из результатов брать поля _id, title и text.

    Метод select в Mongoose, кстати, используется немного не так. Когда вы передаёте в него в качестве параметра объект, нужно значениям указывать либо 1 (поле включено в результат), либо 0 (поле не включается в результат). Если вы передаёте непустую строку в качестве значения, она воспринимается как 1, но это будет искажать ваше понимание того, что происходит. Select — это только указание, какие поля должны быть в результате, этот метод вообще никак не влияет на условие выборки документов из коллекции. Условия выборки указываются в find.
    Ответ написан
    4 комментария
  • Как использовать импортированную переменную в шаблоне?

    В шаблоне доступно все что указано в data (а так же в props и methods) если используете Options API, либо всё что возвращает метод setup() если используете Vue 3 Composition API.

    https://vuejs.org/v2/guide/components.html
    https://v3.vuejs.org/guide/component-basics.html
    https://v3.vuejs.org/guide/composition-api-introdu...

    Кроме того, в вашем случае правильным решением будет завести computed свойство, которое будет генерировать src картинки (склеивать HOST с props.image) и использовать в шаблоне уже его.
    Ответ написан
    2 комментария
  • Фронт и бэк на локалхосте. Как обмениваться данными?

    @rPman
    Отладка и логирование на бакэнде
    Еще смотри что за заголовки у тебя уходят (ты из видишь в инспекторе), может ошибся и не ожидаемые отправляешь?
    Ответ написан
    Комментировать
  • Фронт и бэк на локалхосте. Как обмениваться данными?

    Добавьте заголовки для cors
    res.setHeader("Access-Control-Allow-Origin", "*")
    	res.setHeader("Access-Control-Allow-Credentials", "true");
    	res.setHeader("Access-Control-Allow-Headers", "*");
    	res.setHeader("Access-Control-Expose-Headers", "*");
    	res.setHeader("Access-Control-Request-Headers", "*");
    	res.setHeader( "Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, PATCH, OPTIONS" );

    https://developer.mozilla.org/ru/docs/Web/HTTP/CORS
    Ответ написан
    1 комментарий
  • Авторизация. Как лучше всего ее организовать?

    KulakovAngel
    @KulakovAngel
    Full Stack Developer (Node.JS)
    Давайте для определенности предположим, что имеется стек Express.js (серверная архитектура) + Passport.js (библиотека для авторизации) + Mongoose.js (ODM для бд Mongo) + React.js (клиент). Стоит заметить, что, если нужна именно автономная авторизация и в будущем точно не потребуется OAuth, то Passport можно и не использовать. В данном случае все достаточно просто можно реализовать самостоятельно. Passport необходим (на мой взгляд) в основном для унификации всех способов авторизации.

    Теперь определимся со степенью и способом защиты. Наиболее популярное решение среди токенов - JWT-токены. Смысл их использования в том, что
    • JWT самодостаточный. Имея валидный токен, не нужно дергать базу (как в случае логин/пароль), чтобы проверить права пользователя.
    • JWT можно расшифровать, но нельзя подделать. Если у Васи есть выданный ему токен, он сможет его подделать и прикинуться Ваней, но сервер это узнает и выдаст ошибку.


    Но JWT можно угнать. Как? Например, физически получив доступ к компьютеру пользователя во время активной сессии (открыть консоль браузера и написать короткий код, или посмотрев Redux store, ежели он используется - см. скриншот - это стор моего профиля в одном из онлайн-магазинов).
    600151069e986774161150.jpeg

    Как быть в таком случае? Во-первых, токен имеет время жизни. Когда токен "протухнет", злоумышленнику потребуется ввести логин/пароль, а их он не знает. На самом деле любую систему можно взломать, вопрос лишь в сложности.

    Существует более продвинутая схема. В данной схеме используется два токена: access и refresh. Токен доступа (access) имеет короткое время жизни (обычно 20 минут, но может варьироваться и зависит в основном от интенсивности использования проектируемого веб-приложения: если запросы к серверу выполняются редко, скажем, раз в час - время его жизни можно увеличить), refresh-токен имеет длинное время жизни (несколько дней, а то и недель - опять же зависит от степени защиты/интенсивности использования). При этом, если нам нужно реализовать функционал "запомнить меня" (автологин), refresh будем хранить, скажем, в LocalStorage браузера. При этом рекомендуют не "молча" запоминать пользователя, а сделать флажок на форме авторизации "запомнить меня" - чтобы пользователь понимал, что есть риск угона токена. Кстати, где что хранить на клиенте.
    • Логин/пароль никогда нигде не храним.
    • Access токен хранят в оперативной памяти (в Redux-store или, скажем, в axios.defaults.headers.common).
    • Refresh - если нужна функция автологина - храним в LocalStorage, если не нужна - тоже в оперативной памяти.


    Сделаем авторизацию с автологином. Для этого сессии будем хранить в базе данных, а также дадим пользователю возможность "выйти со всех устройств" и удаления конкретной сессии (кстати, в авторизации Laravel теперь это реализовано по умолчанию). Что хранить в качестве сессии в БД? Это refresh-string + некая информация об устройстве-клиенте (может быть ip, версия браузера, версия ОС, местоположение - все это легко получается сервером из заголовков запроса, местоположение вычисляется по ip). В access-токене можно хранить любую неконфеденциальную информацию (нельзя: пароль, данные кредитных карт и т.д.), я буду хранить просто имя пользователя. Рефреш-токен может представлять собой просто рандомную уникальную строку, но я зашифрую ее дополнительно в JWT, чтобы проверять срок жизни и валидность, не лезя в БД. Итак, приступим (сразу к сути, не пишу про установку пакетов и создание схем для Mongoose и т.п., чтобы пост не получился бесконечным).

    Для начала нам нужно инициализировать Passport.js. Нам нужны две стратегии: локальная для первоначального входа через логин/пароль и JWT. Токен серверу будем передавать через заголовок "authorization".
    passport.use('byUsernameAndPassword', // я так назвал свою стратегию
        new LocalStrategy({
            usernameField: 'username',
            passwordField: 'password',
            // passReqToCallback: true,
            session: false,
        }, (username, password, done) => {
            User
                .verifyByUsernameAndPassword(username, password) // примечание: это, конечно, не базовый метод, он написан "вручную"
                .then(user => done(null, user))
                .catch(error => done(error, false, error.message));
        })
    );
    
    passport.use(
        new JwtStrategy({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),// passport сам дешифрует токен и проверит его валидность, хотя это и не сложно сделать вручную, если решите passport не использовать
            secretOrKey: process.env.ACCESS_SECRET,
            session: false,
        }, ({ username }, done) => {
            // на самом деле можно и не доставать пользователя из базы, просто расшифровать jwt, как я уже писал.
            // Доставать из базы можно только тогда, когда его нужно поменять (скажем, пользователю нужно добавить заказ в корзину)
            // меньше запросов к БД - быстрее работает
            User
                .findOne({ phoneNumber: username })
                .then(user => {
                    if (!user) return done(null, false, 'This user has been deleted or changed. Please, login again.');
                    return done(null, user);
                })
                .catch(err => done(err));
        })
    );
    
    module.exports = passport;


    Далее реализуем следующие маршруты (и контроллеры для них):
    const auth = require('../controller/auth');
    const router = express.Router();
    router.post('/login', passport.authenticate('byUsernameAndPassword'), auth.logIn);
    
    const sessionRouter = express.Router();
    
    // из Refresh-токена извлекаем собственно строку
    function decodeRefreshStringFromToken(refreshToken, errorCallback) {
        const decoded = require('jsonwebtoken').verify(refreshToken, process.env.REFRESH_SECRET, (error, decoded) => {
            return error ? error : decoded;
        });
        if (decoded instanceof Error) // обработка ошибок
        return decoded.refreshToken;
    }
    
    // для любой работы с сессией в теле запроса будем ожидать Refresh-токена
    sessionRouter.use((req, res, next) => {
        if (!req.body.refreshToken) return next(new RefreshTokenUnauthorizedError());
        res.locals.refreshString = decodeRefreshStringFromToken(req.body.refreshToken, next);
        return next();
    });
    
    // выход - удаление сессии из БД
    sessionRouter.post('/logout', auth.logOut); // todo: jwtAuthMiddleware here&&
    // сессия становится неактивной (пользователь закрыл приложение) - зачем нужен данный роут? В БД у сессии будем хранить поле isActive. Если пользователь сидит сразу с двух устройств - подозрительно)
    sessionRouter.post('/interrupt', auth.interruptSession);
    // обновляем сессию, когда время жизни Refresh-токена вышло
    sessionRouter.post('/refresh', auth.refreshSession);
    
    router.use('/session', sessionRouter);
    module.exports = router;


    Контроллеры проектируем "тонкими" (если коротко, контроллер вызывает метод модели, а не 100 ее методов), например:
    module.exports.logIn = async (req, res, next) => {
        req.user.installSession(req.useragent)
            .then(newSessionData => {
                res.json(newSessionData);
                next();
            });
    };


    При этом installSession может выглядеть как-то так (разделен на методы по смыслу, эти же методы используются и в других случаях, например, при рефреше сессии задействуем makeSessionData):
    userSchema.methods.installSession = async function(useragent) {
        const newSession = this.createSession(useragent); // метод реализован ниже
    
        if (this.sessions.length > 5) this.sessions = []; // допустим ограничение максимум в 5 сессий
        if (this.sessions.find(session => session.isActive)) {// выходить из всех активных сессий, может даже удалять их}
        this.sessions.push(newSession);
        await this.save();
        return this.makeSessionData(newSession.refreshToken);
    };
    
    userSchema.methods.createSession = function({ browser, os, platform }) {
        return {
            refreshString: this.generateRefreshString(),
            browser,
            os,
            platform,
        };
    };
    
    userSchema.methods.makeSessionData = function(refreshString) {
        const {
            id,
            name,
            phoneNumber,
            role,
        } = this;
        const refreshToken = this.makeRefreshJwtToken(refreshString);
    
        return ({ id, name, phoneNumber, role, refreshToken, accessToken: this.makeAccessJwtToken({ username: this.username }), tokenType: 'Bearer', expiresIn: process.env.ACCESS_EXPIRES_IN, });
    };
    
    userSchema.methods.makeRefreshJwtToken = function(refreshString) {
        return jwt.sign(
            { refreshString },
            REFRESH_SECRET,
            { expiresIn: REFRESH_EXPIRES_IN });
    };

    Заключение в комментарии к данному ответу (чуть-чуть не уложился в 10 000 символов)
    Ответ написан
    3 комментария
  • Как узнать, что картинка была загружена браузером?

    Stalker_RED
    @Stalker_RED
    Оно из коробки так и работает.
    Возможно у вас url картинки отличается или в заголовках кеш запрещен.
    Покажите заголовки картинки.
    Ответ написан
    5 комментариев
  • Какие Wi-Fi адаптеры без проблем работают в Linux?

    SignFinder
    @SignFinder
    Wintel\Unix Engineer\DevOps
    Описанные на официальной WIKI как минимум
    https://wireless.wiki.kernel.org/en/users/drivers
    Ответ написан
    Комментировать
  • Почему если ключ объекта в кавычках, то он может содержать дефис, а если без, то уже нет?

    Lynn
    @Lynn
    nginx, js, css
    > Ведь в любом случае это строка
    Нет.

    Во втором случае это должен быть «идентификатор», а идентификатор не может содержать дефисы. Так же как и а именах переменных и т.п.

    Вас же не удивляет что нельзя объявить переменную let tg-bot = 42;

    P.S. Хотя есть языки в которых можно использовать дефис в именах переменных…
    Ответ написан
    2 комментария
  • Почему если ключ объекта в кавычках, то он может содержать дефис, а если без, то уже нет?

    Akdmeh
    @Akdmeh
    PHP, Yii2, Music
    Потому что парсер не может понять, что вы хотите - от переменной tg отнять переменную bot, или это строка с дефисом по середине.
    Он может "догадаться", но все же обычно это неоднозначное прочтение кода, поэтому он перестраховывается.
    Ответ написан
    Комментировать
  • Как передать картинку на бэк как бинарный файл?

    Alexandroppolus
    @Alexandroppolus
    кодир
    FileReader тут не нужен.
    Не надо читать содержимое файла, чтобы потом сотворить из него блоб. Просто отправляй исходный файл.
    Ответ написан
  • Как передать картинку на бэк как бинарный файл?

    @zkrvndm
    Софт для автоматизации
    Пример отправки файла, чтобы было как у вас на скриншоте:
    async function sendFile() {
    	
    	// Берём из поля выбора файлов самый первый файл за номером 0:
    	var file = document.querySelector('input[type="file"]').files[0];
    	
    	// Создаем форму конструктором:
    	var form_data = new FormData();
    	
    	// Добавляем в форму наш файл из поля:
    	form_data.append('image', file, file.name);
    	
    	// Добавляем остальные данные:
    	
    	form_data.append('title', 'www');
    	form_data.append('body', 'ghghghghgh');
    	form_data.append('tag', '123456');
    	form_data.append('lang', 'ru');
    	form_data.append('published_at', '2020-01-01 20:00:00');
    	
    	// Отправляем форму на сервер (замените адрес обработчика на свой):
    	
    	var response = await (await fetch('https://nadim.work/test.php', {
    		method: 'POST',
    		body: form_data
    	})).text();
    	
    	// Выводим ответ сервера в консоли:
    	
    	console.log("Ответ сервера: " + response);
    	
    	return response;
    	
    }

    Вызывайте функцию sendFile() и смотрите результат.
    Ответ написан
    3 комментария
  • Как поочерёдно объеденить записи массива?

    Для полноты картины — вариант без предварительного копирования исходного массива и последующего изменения элементов в копии, как у Сергей Соколов, а сразу с заполнением пустого массива готовыми значениями:
    const arr2 = Object.keys(arr).reduce((result, i) => {
        result.push(result.length ? result[i - 1] + '-' + arr[i] : arr[i]);
        return result;
    }, []);
    Ответ написан
    Комментировать
  • Как поочерёдно объеденить записи массива?

    Ответ написан
    Комментировать
  • Как поочерёдно объеденить записи массива?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    const arr2 = arr.slice(); // копия исходного массива
    arr2.reduce((acc, c, i, arr) => arr[i] = acc + '-' + c); // результат reduce() не нужен, важен процесс )
    
    arr2 // [ "media-center", "media-center-events", "media-center-events-current-events" ]
    Ответ написан
    1 комментарий
  • Какой процесор будет лучше: R5 5600X, i7-10700K или R7 3700X?

    Adamos
    @Adamos
    По собственному опыту, у Райзенов могут быть глюки с энергосбережением под Линь (и, как следствие, зависания).
    Лечится отключением С6 в биосе и прописыванием пары слов в свойства загрузки ядра, пять минут времени вместе с гуглением, но новичка может напугать.
    Еще вроде бы не получается запустить Хакинтош на процессорах АМД даже в виртуалке, но это мало кому надо.

    В остальном все плюсы у Райзенов...
    Ответ написан
    2 комментария
  • Какой процесор будет лучше: R5 5600X, i7-10700K или R7 3700X?

    @d-sem
    Живой опыт и ощущения.

    Год назад брал себе домой 3600 / 32gb ram / 500gb nvme. Работаю в Ubuntu. Бекенд разработчик. Иногда балуюсь фронтом в свободное время.

    Считаю, что для задач веб-разработки 3600 за глаза. Ситуации, когда я бы упирался в процессор - очень редки и это были нишевые задачи. Чтобы веб-разработка загрузила современный процессор и уперлась в его производительность это нужно или что-то сделать не так или делать что-то нишевое.

    На прошлых машинах в разработке упирался в память или быстродействие накопителя.

    Отсюда мое личное мнение, что окончательный выбор нужно делать исходя из других задач. Будет много софта оптимизированного под интел - брать интел. Будут задачи на какой нибудь многопоток аля конвертацию видео - брать там где больше ядер. Если игры - вложить в видяхи. Если нет - то лучше вложиться в лучшую материнку, память, охлаждение, корпус, нежели сходить с ума от синтетических тестов.
    Ответ написан
  • Где взять голый linux?

    Sanes
    @Sanes
    Поставь Ubuntu. Не мучайся.
    Ответ написан
    2 комментария
  • Как сделать визуальное изменение картинки?

    w3bsmes
    @w3bsmes
    Куратор тега «Глупые вопросы»
    Ответ написан
    Комментировать
  • Где проверить код на кроссбраузерность?

    Aetae
    @Aetae
    Тлен
    Единственный прикладной вариант - поставить макось в виртуалку. Это, правда, не так просто.

    Вариант на по-быстрому: поставить https://www.falkon.org/download/ - он работает на webkit как и safari, а потому 90% багов совпадают.)
    Ответ написан
    3 комментария