-
Notifications
You must be signed in to change notification settings - Fork 5
Add interviewer question templates and answer keys #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AndroidDevAccount
wants to merge
48
commits into
humancto:master
Choose a base branch
from
AndroidDevAccount:codex/interview-question-templates
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
9685f40
Add interviewer question templates and answer keys
AndroidDevAccount 170e3cc
Show answer keys in an automatic side panel
AndroidDevAccount 5890613
Refresh answer-key panel styles
AndroidDevAccount 11a3684
Document REST exercise with a public test API
AndroidDevAccount e185665
Make activity card draggable and refresh panel styles
AndroidDevAccount a71668f
Expand MVC validation interview exercise
AndroidDevAccount 58fe846
Add sample data to SQL interview exercise
AndroidDevAccount 7dc8335
Document LINQ employee exercise data
AndroidDevAccount 2e05650
Explain SOLID in interviewer answer key
AndroidDevAccount 9b2a022
Replace debugging prompt with easy running sum
AndroidDevAccount c8ceeba
Make interview overview candidate-facing
AndroidDevAccount a147d14
Add Entity Framework migration exercise
AndroidDevAccount 5e4f5ed
Add AI prompting interview exercise
AndroidDevAccount 0781582
Add responsive CSS interview exercise
AndroidDevAccount ec7628b
Protect interview templates behind admin auth
AndroidDevAccount 4ce15a5
Restore admin login and recent session state
AndroidDevAccount 99790e6
Use standard MVC validation in interview exercise
AndroidDevAccount 52d4903
Align SOLID exercise with constructor injection
AndroidDevAccount e144f38
Start interview with insurance OOP model
AndroidDevAccount ee0e4dc
Load candidate interview overview first
AndroidDevAccount aab45c0
Make premium warm-up object-oriented
AndroidDevAccount 5e53e45
Add reusable interview question sets
AndroidDevAccount 6980ed4
Use doubles in premium warm-up
AndroidDevAccount 3651c0b
Simplify policy card CSS exercise
AndroidDevAccount a5f85b2
Expand everyday CSS styling exercise
AndroidDevAccount f562a4e
Clarify mobile CSS media query
AndroidDevAccount 7561365
Consolidate interview controls and add admin management
AndroidDevAccount 96fc69f
Use one grouped question loader
AndroidDevAccount 8651a11
Make dashboard reliably dismissible
AndroidDevAccount 7de724c
Clarify interview question controls
AndroidDevAccount da166e3
Restore activity monitor and session dashboard pill
AndroidDevAccount 51515bb
Position dashboard close button in corner
AndroidDevAccount 3f8ab8b
Turn dashboard into resumable app page
AndroidDevAccount 0c4be4c
Simplify activity window drag handle
AndroidDevAccount 8e97b2a
Fix dashboard resume and session joining
AndroidDevAccount 4df9517
Restore compact question and editor dropdowns
AndroidDevAccount 7d29a1b
Clarify participant roles and add admin pill menu
AndroidDevAccount ff6f598
Persist server-backed interview question sets
AndroidDevAccount 89ae36b
Consolidate connected user display
AndroidDevAccount 0e2d682
Use standard activity drag icon
AndroidDevAccount f5cf73e
Load question sets at their introduction
AndroidDevAccount dcc2d11
Restore answer key for loaded questions
AndroidDevAccount 07dc374
Hide session sharing from candidates
AndroidDevAccount 354e99f
Address interview UI review feedback
AndroidDevAccount e4d06a2
Address follow-up PR review findings
AndroidDevAccount 467ae02
Make admin creation atomic
AndroidDevAccount 4f28131
Enable C sharp code execution
AndroidDevAccount c9c2261
Revert "Enable C sharp code execution"
AndroidDevAccount File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| 'use strict'; | ||
|
|
||
| const admin = require('firebase-admin'); | ||
| const bcrypt = require('bcryptjs'); | ||
| const jwt = require('jsonwebtoken'); | ||
|
|
||
| if (!admin.apps.length && process.env.FIREBASE_PROJECT_ID) { | ||
| admin.initializeApp({ | ||
| credential: admin.credential.cert({ | ||
| projectId: process.env.FIREBASE_PROJECT_ID, | ||
| clientEmail: process.env.FIREBASE_CLIENT_EMAIL, | ||
| privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\n/g, '\n') | ||
| }), | ||
| databaseURL: process.env.FIREBASE_DATABASE_URL | ||
| }); | ||
| } | ||
|
|
||
| function emailKey(email) { | ||
| return Buffer.from(email.trim().toLowerCase()).toString('base64url'); | ||
| } | ||
|
|
||
| function requireAdmin(req) { | ||
| const header = req.headers.authorization || ''; | ||
| if (!header.startsWith('Bearer ') || !process.env.JWT_SECRET) { | ||
| throw Object.assign(new Error('Authentication required'), { status: 401 }); | ||
| } | ||
| const decoded = jwt.verify(header.slice(7), process.env.JWT_SECRET); | ||
| if (decoded.isAdmin !== true) { | ||
| throw Object.assign(new Error('Admin access required'), { status: 403 }); | ||
| } | ||
| return decoded; | ||
| } | ||
|
|
||
| module.exports = async function handler(req, res) { | ||
| res.setHeader('Cache-Control', 'private, no-store'); | ||
| res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); | ||
| res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); | ||
|
|
||
| if (req.method === 'OPTIONS') return res.status(200).end(); | ||
| if (!['GET', 'POST'].includes(req.method)) { | ||
| return res.status(405).json({ error: 'Method not allowed' }); | ||
| } | ||
|
|
||
| try { | ||
| const currentAdmin = requireAdmin(req); | ||
| if (!admin.apps.length) { | ||
| return res.status(503).json({ error: 'Admin storage is not configured' }); | ||
| } | ||
| const adminsRef = admin.database().ref('adminAccounts'); | ||
| const ownerEmail = String(process.env.ADMIN_EMAIL || '').trim().toLowerCase(); | ||
|
|
||
| if (req.method === 'GET') { | ||
| const snapshot = await adminsRef.once('value'); | ||
| const additionalAdmins = Object.values(snapshot.val() || {}).map(account => ({ | ||
| email: account.email, | ||
| createdAt: account.createdAt, | ||
| createdBy: account.createdBy, | ||
| disabled: account.disabled === true, | ||
| owner: false | ||
| })); | ||
| const owner = ownerEmail | ||
| ? [{ email: ownerEmail, owner: true }] | ||
| : []; | ||
| return res.status(200).json({ admins: [...owner, ...additionalAdmins] }); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| if (!ownerEmail || String(currentAdmin.email || '').trim().toLowerCase() !== ownerEmail) { | ||
| return res.status(403).json({ error: 'Only the configured owner can create admins' }); | ||
| } | ||
|
|
||
| const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body; | ||
| const email = String(body?.email || '').trim().toLowerCase(); | ||
| const password = String(body?.password || ''); | ||
| if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { | ||
| return res.status(400).json({ error: 'Enter a valid email address' }); | ||
| } | ||
| if (password.length < 8) { | ||
| return res.status(400).json({ error: 'Password must be at least 8 characters' }); | ||
| } | ||
| if (email === ownerEmail) { | ||
| return res.status(409).json({ error: 'That admin already exists' }); | ||
| } | ||
|
|
||
| const accountRef = adminsRef.child(emailKey(email)); | ||
| const account = { | ||
| email, | ||
| passwordHash: await bcrypt.hash(password, 12), | ||
| createdAt: Date.now(), | ||
| createdBy: currentAdmin.email, | ||
| disabled: false | ||
| }; | ||
| const result = await accountRef.transaction(currentValue => { | ||
| if (currentValue !== null) return; | ||
| return account; | ||
| }); | ||
| if (!result.committed) { | ||
| return res.status(409).json({ error: 'That admin already exists' }); | ||
| } | ||
|
|
||
| return res.status(201).json({ success: true, admin: { email, owner: false } }); | ||
| } catch (error) { | ||
| if (error instanceof SyntaxError) { | ||
| return res.status(400).json({ error: 'Invalid JSON body' }); | ||
| } | ||
| const authError = ['JsonWebTokenError', 'TokenExpiredError', 'NotBeforeError'] | ||
| .includes(error.name); | ||
| const status = error.status || (authError ? 401 : 500); | ||
| if (status === 500) console.error('Admin management error:', error); | ||
| return res.status(status).json({ | ||
| error: status === 500 ? 'Admin management failed' : error.message | ||
| }); | ||
| } | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.