diff --git a/.env-template b/.env-template index bf892ed3..8a5efa7d 100644 --- a/.env-template +++ b/.env-template @@ -3,6 +3,8 @@ NODE_ENV=development PORT=3001 AIRTABLE_API_KEY= /* YOUR API KEY HERE */ AIRTABLE_APP_ID= /* GLADEO AIRTABLE APP ID HERE */ +EMAIL= /* GLADEO EMAIL HERE */ +PASSWORD= /* GLADEO EMAIL PASSWORD HERE */ CLIENT_ID= /* GLADEO GMAIL CLIENT ID HERE -- can be found in Notion API Docs */ CLIENT_SECRET= /* GLADEO GMAIL CLIENT SECRET HERE -- can be found in Notion API Docs */ REFRESH_TOKEN= /* GLADEO GMAIL REFRESH_TOKEN HERE -- can be found in Notion API Docs */ diff --git a/package.json b/package.json index 8d7d6322..75245b34 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ ] }, "dependencies": { + "@google-cloud/local-auth": "^0.1.0", "@react-native-community/async-storage": "^1.9.0", "@react-native-community/cameraroll": "^1.6.0", "@react-native-community/masked-view": "0.1.5", @@ -50,11 +51,13 @@ "core-util-is": "^1.0.2", "dotenv": "^8.2.0", "express-session": "^1.17.0", - "googleapis": "^49.0.0", + "google-auth-library": "^6.0.0", + "googleapis": "^50.0.0", "mem": "^6.0.1", + "nodemailer": "^6.4.2", + "opn": "^6.0.0", + "react": "~16.9.0", "metro-react-native-babel-preset": "^0.59.0", - "nodemailer": "^6.4.6", - "react": "16.9.0", "react-dom": "16.9.0", "react-native": "~0.61.4", "react-native-animated-ellipsis": "^2.0.0", diff --git a/server/api/routes/user.js b/server/api/routes/user.js index a16228b2..1201e97e 100644 --- a/server/api/routes/user.js +++ b/server/api/routes/user.js @@ -1,6 +1,6 @@ const express = require('express') const router = express.Router() -const { updateAnsweredQuestions } = require('../../data_access_layer/user') +const { updateAnsweredQuestions, videoAuthorize } = require('../../data_access_layer/user') const { getQuestion, getAllQuestions } = require('../../data_access_layer/question') router.get('/', async (req, res) => { @@ -189,5 +189,20 @@ router.delete('/questions', async (req, res) => { } }) +router.post('/upload', async (req, res) => { + const name = req.body['Name'] + const email = req.body['Email'] + const URI = req.body['URI'] + try { + return res.status(200).send(videoAuthorize(name, email, URI)) + + }catch(err) { + console.log(err) + return res.status(err.statusCode).send(err) + } + + +}) + module.exports = router diff --git a/server/data_access_layer/user.js b/server/data_access_layer/user.js index 8c28bcfd..e8f5ea76 100644 --- a/server/data_access_layer/user.js +++ b/server/data_access_layer/user.js @@ -4,6 +4,16 @@ const { getCompany } = require('./company') const nodemailer = require('nodemailer') const { google } = require('googleapis') const { extractContentFromRecords, getAllFromTable } = require('./helpers') +const fs = require('fs') +const readline = require('readline') +const {google} = require('googleapis') +const youtube = google.youtube('v3') +const OAuth2 = google.auth.OAuth2 +const SCOPES = [ 'https://www.googleapis.com/auth/youtube.upload', + 'https://www.googleapis.com/auth/youtube'] +const TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH || +process.env.USERPROFILE) + '/.credentials/' +const TOKEN_PATH = TOKEN_DIR + 'youtube-nodejs-quickstart.json' async function getUser(userId) { const userRecords = base('Users').select({ @@ -114,7 +124,7 @@ async function sendPasswordResetEmail(email, fullName) { service: 'Gmail', auth: { type: 'OAuth2', - user: 'gladeo.app@gmail.com', + user: process.env.EMAIL, clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, refreshToken: REFRESH_TOKEN, @@ -124,7 +134,7 @@ async function sendPasswordResetEmail(email, fullName) { // Message contents const info = { - from: '"Gladeo" ', + from: '"Gladeo" <' + process.env.EMAIL + '>', to: email, subject: 'Gladeo Password Reset', generateTextFromHTML: true, @@ -175,6 +185,121 @@ async function updateUserPassword(email, password) { return true } + +function storeToken(token) { + try { + fs.mkdirSync(TOKEN_DIR) + } catch (err) { + if (err.code != 'EEXIST') { + throw err + } + } + fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => { + if (err) throw err + console.log('Token stored to ' + TOKEN_PATH) + }) +} + +function getNewToken(oauth2Client, callback) { + const authUrl = oauth2Client.generateAuthUrl({ + access_type: 'offline', + scope: SCOPES + }) + console.log('Authorize this app by visiting this url: ', authUrl) + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }) + rl.question('Enter the code from that page here: ', function(code) { + rl.close() + oauth2Client.getToken(code, function(err, token) { + if (err) { + console.log('Error while trying to retrieve access token', err) + return + } + oauth2Client.credentials = token + storeToken(token) + callback(oauth2Client) + }) + }) +} + +function authorize(credentials, callback, name, email, URI) { + const clientSecret = process.env.CLIENT_SECRET + const clientId = credentials.web.client_id + const redirectUrl = credentials.web.redirect_uris[0] + const oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl) + // Check if we have previously stored a token. + fs.readFile(TOKEN_PATH, function(err, token) { + if (err) { + getNewToken(oauth2Client, callback) + } else { + oauth2Client.credentials = JSON.parse(token) + return(callback(oauth2Client, name, email, URI)) + } + }) +} + +async function uploadVideo(auth, name, email, URI) { + google.options({auth}) + const fileSize = fs.statSync(URI).size + try { + const res = await youtube.videos.insert( + { + part: 'id,snippet,status', + notifySubscribers: false, + requestBody: { + snippet: { + title: '[Draft - ready for review] video by ' + name, + description: 'Draft video created by ' + name + '\nEmail: ' + email, + }, + status: { + privacyStatus: 'private', + }, + }, + media: { + body: fs.createReadStream(URI), + }, + }, + + { + // Use the `onUploadProgress` event from Axios to track the + // number of bytes uploaded to this point. + onUploadProgress: evt => { + const progress = (evt.bytesRead / fileSize) * 100 + readline.clearLine(process.stdout, 0) + readline.cursorTo(process.stdout, 0, null) + process.stdout.write(`${Math.round(progress)}% complete`) + }, + } + ) + + console.log(res.data) + return res.data + + }catch(err) { + console.log(err) + } + +} + + +async function videoAuthorize(name, email, URI) { + name = name + URI = URI + email = email + fs.readFile('client_secret.json', function processClientSecrets(err, content) { + if (err) { + console.log('Error loading client secret file: ' + err) + return + } + // Authorize a client with the loaded credentials, then call the YouTube API. + authorize(JSON.parse(content), uploadVideo, name, email, URI) + }) + +} + + module.exports = { getUser, getUserByEmail, @@ -184,5 +309,10 @@ module.exports = { sendPasswordResetEmail, verifyPasswordCode, updateUserPassword, - updateEmailandPassword + updateEmailandPassword, + uploadVideo, + authorize, + getNewToken, + storeToken, + videoAuthorize } diff --git a/yarn.lock b/yarn.lock index a8b0c65a..95157a50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1241,6 +1241,16 @@ dependencies: "@types/hammerjs" "^2.0.36" +"@google-cloud/local-auth@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@google-cloud/local-auth/-/local-auth-0.1.0.tgz#8f74491e56cbd45a36c2fc23cd257fc63964905c" + integrity sha512-AVhTXbUBH/KBgqzgNWJKMUk2gnCxnXb0dwkCiFv00pgYWm35uc7XYkTrt0pSkp+6uqDWOdt2ChYh1i4TuJTX5A== + dependencies: + arrify "^2.0.1" + google-auth-library "^6.0.0" + open "^7.0.3" + server-destroy "^1.0.1" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -2216,7 +2226,7 @@ array.prototype.flat@^1.2.1: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -arrify@^2.0.0: +arrify@^2.0.0, arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== @@ -4665,9 +4675,9 @@ gaxios@^3.0.0: node-fetch "^2.3.0" gcp-metadata@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.0.1.tgz#e97800c4f375e6218707cc4d90dc151ed5c2f960" - integrity sha512-hX2Ge7RJqESJ3gTfitpJWbdXqxxzgUFX5KVPhE02l9jq17rKKsTeB5h7v7Cky7yhLPfMYcirojC6VWU26lVyCQ== + version "4.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.1.0.tgz#8b9b5903882076948554af471c838e7ea2f564b4" + integrity sha512-r57SV28+olVsflPlKyVig3Muo/VDlcsObMtvDGOEtEJXj+DDE8bEl0coIkXh//hbkSDTvo+f5lbihZOndYXQQQ== dependencies: gaxios "^3.0.0" json-bigint "^0.3.0" @@ -4816,25 +4826,26 @@ google-p12-pem@^3.0.0: dependencies: node-forge "^0.9.0" -googleapis-common@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/googleapis-common/-/googleapis-common-4.1.0.tgz#31d4bbc95b9d8837be1e03024efbd8dd51221dac" - integrity sha512-/T+GflstRAbXbf33zeutDXhXj25I1gKwaWNJ4kWSJVSVpXIZugOX8K7iq3uFCQCOJzehPuPLAC2u8iEf6jN0dw== + +googleapis-common@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/googleapis-common/-/googleapis-common-4.1.1.tgz#433069ce463e50c631c5e1b5871f9d44881c90af" + integrity sha512-Br3EPgm1f7Zb/Vi8R+1Jc5jjhaFZMgQpvGaGAKcBq81BAi7m8NCHBaaR0XF3p4RbMjA4Wiz1RkPD0A8/5aP+xw== dependencies: extend "^3.0.2" gaxios "^3.0.0" google-auth-library "^6.0.0" qs "^6.7.0" url-template "^2.0.8" - uuid "^7.0.0" + uuid "^8.0.0" -googleapis@^49.0.0: - version "49.0.0" - resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-49.0.0.tgz#e2e8bfc070dc1c09aa77fdd842bddd685d9ffb11" - integrity sha512-UoUuDbOzLxtU6fZnDyj6IvYvczBYP08RK3Sn0AknssQJceUYiHldaCjEFT1PDcT9MiKSJrbPze6PRdzQDny0Xw== +googleapis@^50.0.0: + version "50.0.0" + resolved "https://registry.yarnpkg.com/googleapis/-/googleapis-50.0.0.tgz#861aacb96190f9a19dec19c08f0858369ecc1f4e" + integrity sha512-AJdOpPbDiNauraQZr51RR85cA53ygYtzLUj6Nq0cTr3g+GQ+GxjyLT6IMGgSE+/0dGLTpRVCemkZjZQA0XMb5Q== dependencies: google-auth-library "^6.0.0" - googleapis-common "^4.0.0" + googleapis-common "^4.1.0" got@^9.6.0: version "9.6.0" @@ -5354,6 +5365,11 @@ is-directory@^0.3.1: resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= +is-docker@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" + integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6965,7 +6981,12 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0, mime@^2.4.1: +mime@^2.2.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.5.tgz#d8de2ecb92982dedbb6541c9b6841d7f218ea009" + integrity sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w== + +mime@^2.4.1: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== @@ -7463,11 +7484,26 @@ open@^6.2.0: dependencies: is-wsl "^1.1.0" +open@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" + integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + opencollective-postinstall@^2.0.0, opencollective-postinstall@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== +opn@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" + integrity sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ== + dependencies: + is-wsl "^1.1.0" + optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7995,6 +8031,11 @@ qs@^6.5.1, qs@^6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== +qs@^6.7.0: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -8948,6 +8989,11 @@ serve-static@1.14.1, serve-static@^1.13.1: parseurl "~1.3.3" send "0.17.1" +server-destroy@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" + integrity sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0= + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -10055,10 +10101,10 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +uuid@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== v8-compile-cache@^2.0.3: version "2.1.0"