diff --git a/.firebaserc b/.firebaserc index 9862bb42..09324339 100644 --- a/.firebaserc +++ b/.firebaserc @@ -1,7 +1,5 @@ { "projects": { - "default": "parallel-cf800", - "prod": "parallel-cf800", - "beta": "parallel-beta-31dc4" + "main": "parallel-cf800" } -} +} \ No newline at end of file diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml new file mode 100644 index 00000000..db0c31f2 --- /dev/null +++ b/.github/workflows/beta.yml @@ -0,0 +1,40 @@ +name: Deploy Beta + +on: + workflow_dispatch: + pull_request: + types: [edited] + +jobs: + deploy-beta: + name: Deploy + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Use Node.js version from .nvmrc + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + + - name: Write service account + run: | + mkdir private + echo "$BETA_FIREBASE_SERVICE_ACCOUNT" > ./private/service-account.json + echo '${{ secrets.BETA_FIREBASE_SERVICE_ACCOUNT }}' > gcloud.json + env: + BETA_FIREBASE_SERVICE_ACCOUNT: ${{ secrets.BETA_FIREBASE_SERVICE_ACCOUNT }} + shell: bash + + - name: Install + run: | + npm install + shell: bash + + - name: Deploy + env: + GOOGLE_APPLICATION_CREDENTIALS: gcloud.json + run: | + npm run deploy + shell: bash diff --git a/functions/index.js b/functions/index.js index 20775c9b..59b7d258 100644 --- a/functions/index.js +++ b/functions/index.js @@ -4,9 +4,13 @@ const firebase = require('firebase-admin'); +const serviceAccount = require('./build/service-account.json'); + +const isProdProject = !serviceAccount.project_id.includes('beta'); + firebase.initializeApp({ - credential: firebase.credential.cert(require('./build/service-account.json')), - databaseURL: `https://parallel-cf800.firebaseio.com`, + credential: firebase.credential.cert(serviceAccount), + databaseURL: isProdProject ? `https://parallel-cf800.firebaseio.com` : `https://parallel-beta-31dc4.firebaseio.com`, }); const fs = require('fs'); @@ -95,6 +99,16 @@ app.use(user.getActiveUser); app.use((req, res, next) => { res.locals.isProduction = process.env.NODE_ENV === 'production'; + res.locals.isProdProject = isProdProject; + res.locals.firebaseClientConfig = isProdProject ? { + apiKey: "AIzaSyCrQ_PdH-05lcNWETGvGfiwO3MBXk_WeVU", + projectId: "parallel-cf800", + authDomain: "parallel-cf800.firebaseapp.com", + } : { + apiKey: "AIzaSyDk4_ME-Uy1D3Yjg94Af7Gzhg3I1xNYWp8", + projectId: "parallel-beta-31dc4", + authDomain: "parallel-beta-31dc4.firebaseapp.com", + }; res.locals.user = req.user; res.locals.profileComplete = isProfileComplete(req.user); res.locals.pages = {}; diff --git a/functions/views/_layout.pug b/functions/views/_layout.pug index 2d5f7c62..505ae0c1 100644 --- a/functions/views/_layout.pug +++ b/functions/views/_layout.pug @@ -31,8 +31,15 @@ html if isProduction include scripts/newrelic.html + script!= 'window.FIREBASE_CLIENT_CONFIG = ' + JSON.stringify(firebaseClientConfig) + ';' + body #vue + if !isProdProject + .beta + div You are on our Beta Testing Site! + div The information & events on this page are for testing purposes and should not be followed. + a(href="https://parallel.org.uk") Click here to return to the production site. .headerWrapper .header if !sidebarDisabled diff --git a/package.json b/package.json index f0cb7f98..23a7bca7 100644 --- a/package.json +++ b/package.json @@ -7,20 +7,21 @@ "url": "https://github.com/mathigon/parallel.git" }, "scripts": { - "start": "grunt build && npm run serve", - "serve": "grunt concurrent & GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase serve --project prod", - "export-users": "GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase auth:export ./private/tmp-users.json --format=json --project prod", - "deploy": "grunt && GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase deploy --project prod", - "convert-teachers": "node scripts/convert-teachers.js", - "convert-students": "node scripts/convert-students.js", - "get-users": "node scripts/get-users.js", - "get-points": "node scripts/get-students-points.js", - "get-results": "node scripts/get-results.js", - "get-pg-meta-json": "grunt build && node scripts/get-pg-meta-json.js", - "get-tts-results": "node scripts/get-tts-results.js", - "update-results": "node scripts/update-results.js", - "start-marsh": "grunt build && concurrently \"grunt concurrent\" \"GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase serve --project prod\"", - "postinstall": "npx firebase logout && echo 'Logging out, no longer using firebase login, use service accounts instead.'" + "set-firebase": "node scripts/set-firebase.js", + "start": "npm run set-firebase && grunt build && npm run serve", + "serve": "npm run set-firebase && grunt concurrent & GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase serve --project main", + "export-users": "npm run set-firebase && GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase auth:export ./private/tmp-users.json --format=json --project main", + "deploy": "npm run set-firebase && grunt && GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase deploy --project main", + "convert-teachers": "npm run set-firebase && node scripts/convert-teachers.js", + "convert-students": "npm run set-firebase && node scripts/convert-students.js", + "get-users": "npm run set-firebase && node scripts/get-users.js", + "get-points": "npm run set-firebase && node scripts/get-students-points.js", + "get-results": "npm run set-firebase && node scripts/get-results.js", + "get-pg-meta-json": "npm run set-firebase && grunt build && node scripts/get-pg-meta-json.js", + "get-tts-results": "npm run set-firebase && node scripts/get-tts-results.js", + "update-results": "npm run set-firebase && node scripts/update-results.js", + "start-marsh": "npm run set-firebase && grunt build && concurrently \"grunt concurrent\" \"GOOGLE_APPLICATION_CREDENTIALS=private/service-account.json firebase serve --project main\"", + "postinstall": "npm run set-firebase && npx firebase logout && echo 'Logging out, no longer using firebase login, use service accounts instead.'" }, "devDependencies": { "@babel/core": "^7.15.0", diff --git a/scripts/change-email.js b/scripts/change-email.js index be036f77..73116e92 100644 --- a/scripts/change-email.js +++ b/scripts/change-email.js @@ -1,16 +1,12 @@ const fb = require("firebase-admin"); -const serviceAccount = require("../private/service-account.json"); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // This script will change an account email. const from = 'abianch@sunburymanor.surrey.sch.uk' const to = 'abianchi@sunburymanor.surrey.sch.uk' -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: "https://parallel-cf800.firebaseio.com", -}); - const run = () => fb .auth() diff --git a/scripts/convert-students.js b/scripts/convert-students.js index d55c3a4e..7bbf69b6 100644 --- a/scripts/convert-students.js +++ b/scripts/convert-students.js @@ -1,17 +1,13 @@ const path = require('path'); const fs = require('fs'); const fb = require('firebase-admin'); -const serviceAccount = require('../private/service-account.json'); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); const STUDENTS = [ "aaspland@sjl.herts.sch.uk" ]; -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); - const file = path.join(__dirname, `../private/tmp-users.json`); const accounts = JSON.parse(fs.readFileSync(file)).users; diff --git a/scripts/convert-teachers.js b/scripts/convert-teachers.js index 7e0ec437..6909325b 100644 --- a/scripts/convert-teachers.js +++ b/scripts/convert-teachers.js @@ -1,18 +1,14 @@ const path = require('path'); const fs = require('fs'); const fb = require('firebase-admin'); -const serviceAccount = require('../private/service-account.json'); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); const TEACHERS = [ "aaspland@sjl.herts.sch.uk", "qqqqqq.qqqqq@qqq.edu" ]; -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); - const file = path.join(__dirname, `../private/tmp-users.json`); const accounts = JSON.parse(fs.readFileSync(file)).users; diff --git a/scripts/delete-accounts.js b/scripts/delete-accounts.js index 2b6bafbb..196b2d0d 100644 --- a/scripts/delete-accounts.js +++ b/scripts/delete-accounts.js @@ -1,15 +1,11 @@ const fb = require('firebase-admin'); -const serviceAccount = require('../private/service-account.json'); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); const USERS = [ // ADD EMAILS HERE ]; -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); - async function deleteAccount(email) { let id = null; diff --git a/scripts/recover-account.js b/scripts/recover-account.js index 0b2b5967..0e6c04a4 100644 --- a/scripts/recover-account.js +++ b/scripts/recover-account.js @@ -1,5 +1,6 @@ const fb = require("firebase-admin"); -const serviceAccount = require("../private/service-account.json"); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // This script will reset passwords. Used when students are unable to receive emails. @@ -25,11 +26,6 @@ const makePassword = () => { return result; }; -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: "https://parallel-cf800.firebaseio.com", -}); - const updateUserPassword = (email) => fb .auth() diff --git a/scripts/set-account-level.js b/scripts/set-account-level.js index 500d681e..bc599be4 100644 --- a/scripts/set-account-level.js +++ b/scripts/set-account-level.js @@ -1,5 +1,6 @@ const fb = require("firebase-admin"); -const serviceAccount = require("../private/service-account.json"); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // This script will convert a standard account into an admin account. @@ -9,11 +10,6 @@ const isEuclidAdmin = true; ////////////////////////////////////// -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: "https://parallel-cf800.firebaseio.com", -}); - const setAdmin = () => { const claims = {}; diff --git a/scripts/set-firebase.js b/scripts/set-firebase.js new file mode 100644 index 00000000..4347725f --- /dev/null +++ b/scripts/set-firebase.js @@ -0,0 +1,17 @@ +const fs = require("fs"); +const serviceAccount = require("../private/service-account.json"); + +const isProdProject = !serviceAccount.project_id.includes("beta"); + +fs.writeFileSync( + ".firebaserc", + JSON.stringify( + { + projects: { + main: isProdProject ? "parallel-cf800" : "parallel-beta-31dc4", + }, + }, + null, + 2, + ), +); diff --git a/scripts/switch-data.js b/scripts/switch-data.js index 70d21453..2b98f550 100644 --- a/scripts/switch-data.js +++ b/scripts/switch-data.js @@ -1,5 +1,6 @@ const fb = require("firebase-admin"); -const serviceAccount = require("../private/service-account.json"); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // Their old account with the data const FROM_EMAIL = "ssenthilkumar.pingle@deferrerstrust.com"; @@ -8,11 +9,6 @@ const FROM_EMAIL = "ssenthilkumar.pingle@deferrerstrust.com"; const TO_EMAIL = "ftilley.tpa@affinitylp.co.uk"; ////// -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: "https://parallel-cf800.firebaseio.com", -}); - const getAnswerCount = (data) => data.answers ? Object.keys(data.answers).length : 0; diff --git a/scripts/update-euclid-data.js b/scripts/update-euclid-data.js index d724d5a5..087a7638 100644 --- a/scripts/update-euclid-data.js +++ b/scripts/update-euclid-data.js @@ -2,7 +2,8 @@ const fs = require('fs'); const path = require('path'); const fb = require('firebase-admin'); const csv = require("csvtojson"); -const serviceAccount = require('../private/service-account.json'); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // -------- @@ -13,11 +14,6 @@ const dryRun = true; // when true, will just log what it will write, but not sav // -------- // here be dragons // - -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); const userDb = fb.firestore().collection('users'); const updatableFields = [ diff --git a/scripts/update-results.js b/scripts/update-results.js index d4b77d42..d54b2cbe 100644 --- a/scripts/update-results.js +++ b/scripts/update-results.js @@ -1,5 +1,6 @@ const fb = require('firebase-admin'); -const serviceAccount = require('../private/service-account.json'); +const initializeFirebase = require("./utils/initializeFirebase"); +initializeFirebase(); // --------------------- @@ -10,11 +11,6 @@ const THEN_ANSWER = '216'; const ADD_POINTS = 2; // --------------------- - -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); const userDb = fb.firestore().collection('users'); async function updateAnswers() { diff --git a/scripts/utils/downloadUsers.js b/scripts/utils/downloadUsers.js index 9147c293..0f174ecb 100644 --- a/scripts/utils/downloadUsers.js +++ b/scripts/utils/downloadUsers.js @@ -3,12 +3,8 @@ const exec = util.promisify(require('child_process').exec); const path = require('path'); const fs = require('fs'); const fb = require('firebase-admin'); -const serviceAccount = require('../../private/service-account.json'); - -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); +const initializeFirebase = require("./initializeFirebase"); +initializeFirebase(); const cacheFilePath = path.join(__dirname, `../../private/cache-users.json`); diff --git a/scripts/utils/initializeFirebase.js b/scripts/utils/initializeFirebase.js new file mode 100644 index 00000000..0415ab4c --- /dev/null +++ b/scripts/utils/initializeFirebase.js @@ -0,0 +1,13 @@ +const fb = require("firebase-admin"); +const serviceAccount = require("../private/service-account.json"); + +const isProdProject = !serviceAccount.project_id.includes("beta"); + +const initializeFirebase = () => { + fb.initializeApp({ + credential: fb.credential.cert(serviceAccount), + databaseURL: isProdProject ? `https://parallel-cf800.firebaseio.com` : `https://parallel-beta-31dc4.firebaseio.com`, + }); +} + +module.exports = initializeFirebase; diff --git a/scripts/yearly-updates/delete-old-users.js b/scripts/yearly-updates/delete-old-users.js index 3e795d14..247abdff 100644 --- a/scripts/yearly-updates/delete-old-users.js +++ b/scripts/yearly-updates/delete-old-users.js @@ -1,12 +1,8 @@ const fs = require("fs"); const path = require("path"); const fb = require("firebase-admin"); -const serviceAccount = require("../../private/service-account.json"); - -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: "https://parallel-cf800.firebaseio.com", -}); +const initializeFirebase = require("../utils/initializeFirebase"); +initializeFirebase(); const keysToRemove = [ "first", diff --git a/scripts/yearly-updates/update-users-upload.js b/scripts/yearly-updates/update-users-upload.js index 484cb012..1bb861aa 100644 --- a/scripts/yearly-updates/update-users-upload.js +++ b/scripts/yearly-updates/update-users-upload.js @@ -1,15 +1,12 @@ const fs = require('fs'); const path = require('path'); const fb = require('firebase-admin'); -const serviceAccount = require('../../private/service-account.json'); -const promiseLimit = require('promise-limit') +const initializeFirebase = require("../utils/initializeFirebase"); +const promiseLimit = require('promise-limit'); -const limit = promiseLimit(400); +initializeFirebase(); -fb.initializeApp({ - credential: fb.credential.cert(serviceAccount), - databaseURL: 'https://parallel-cf800.firebaseio.com' -}); +const limit = promiseLimit(400); const userDb = fb.firestore().collection('users'); diff --git a/static/pages.yaml b/static/pages.yaml index edc3b0dc..8fd87441 100644 --- a/static/pages.yaml +++ b/static/pages.yaml @@ -1288,7 +1288,7 @@ test: title: Parallel Academy Year 7 Summer Term Test postSubmissionMessage: Thank you, we have received your answers. Please await further instructions from your tutor in Zoom. available: 2024-01-15T13:00:00 - answersVisibleFrom: 2025-07-21T09:00:00 + answersVisibleFrom: 2024-07-21T09:00:00 deadline: 2025-08-31T21:00:00 - url: academy-8sum-test title: Parallel Academy Year 8 Summer Term Test diff --git a/static/scripts/main.js b/static/scripts/main.js index 83af11d6..048f2afb 100755 --- a/static/scripts/main.js +++ b/static/scripts/main.js @@ -58,11 +58,7 @@ document.addEventListener("wheel", function(event){ }); document.addEventListener('DOMContentLoaded', function() { - firebase.initializeApp({ - apiKey: "AIzaSyCrQ_PdH-05lcNWETGvGfiwO3MBXk_WeVU", - projectId: "parallel-cf800", - authDomain: "parallel-cf800.firebaseapp.com", - }); + firebase.initializeApp(window.FIREBASE_CLIENT_CONFIG); const user = getUser(); const challenge = window.PARALLELOGRAM ? getChallenge() : null; diff --git a/static/styles/layout.less b/static/styles/layout.less index c9b84f26..96759e61 100644 --- a/static/styles/layout.less +++ b/static/styles/layout.less @@ -23,6 +23,17 @@ html { line-height: 1.8; } +.beta { + background-color: orange; + margin: 0px -1rem; + text-align: center; + + a { + color: blue; + text-decoration: underline; + } +} + #logo { background: url('/images/logo.png'); background-size: cover;