diff --git a/.github/steps/1-step.md b/.github/steps/1-step.md
index 56cac05..ba72708 100644
--- a/.github/steps/1-step.md
+++ b/.github/steps/1-step.md
@@ -89,13 +89,13 @@ Lets add a simple banner feature for teachers to make announcements and then ask
1. In VS Code, open the source control panel and make note of the uncommitted changes.
-1. Hover over the **Changes** section to show various icons. Click the **Code Review** button.
+1. Hover over the **Changes** section to show various icons. Click the **Code Review** button and wait a moment for Copilot to add comments.
> 💡 **TIP:** There are 3 levels of review available: `unstaged changes` and `staged changes` and `uncommitted changes`
-1. In VS Code, open the **Comments** panel to find a list of review feedback from Copilot.
+1. Expand the **Comments** panel to find a list of review feedback from Copilot.
diff --git a/.github/steps/2-step.md b/.github/steps/2-step.md
index 54a3888..9c20ead 100644
--- a/.github/steps/2-step.md
+++ b/.github/steps/2-step.md
@@ -22,19 +22,23 @@ For more information, see the [GitHub Copilot code review documentation](https:/
### ⌨️ Activity: Request a review
-1. If needed, open a another tab and for this exercise repository.
+1. If needed, open a another tab for this exercise repository.
1. Start a new pull request. Enter the following details and click the **Create pull request** button.
- **compare:** `add-announcement-banner`
- **target:** `main`
- - **title:** `Add Announcement Banner`
+ - **title:** `Add announcement banner`
1. In the right-side details area, find the **Reviewers** menu. Click on the **settings icon** to show a list of available reviewers and select **Copilot**.
-1. Wait a moment for Copilot to review the changes and add comments to your pull request.
+1. Wait a moment for Copilot to review the changes and add comments to your pull request. Notice an entry was added to the conversation log.
+
+
+
+
1. With the review requested, wait a moment for Mona to check your work, provide feedback, and share the next lesson.
diff --git a/.github/steps/3-step.md b/.github/steps/3-step.md
index d029c0e..43dd460 100644
--- a/.github/steps/3-step.md
+++ b/.github/steps/3-step.md
@@ -4,7 +4,7 @@ The school's coding standards are crucial for maintaining the activities website
### 📖 Theory: Repository Custom Instructions
-Repository custom instructions allow you to provide Copilot with context about your project standards and preferences. By creating instruction files, you can ensure Copilot's suggestions consistently follow your team's conventions and focus on your specific requirements.
+Repository custom instructions allow you to provide Copilot with context about your project standards and preferences. By creating instruction files, you can ensure Copilot's suggestions consistently follow your team's conventions and focus on your specific requirements. You can even have copilot analyze your project and [generate instructions](https://code.visualstudio.com/docs/copilot/customization/custom-instructions#_generate-an-instructions-file-for-your-workspace) for you!
**Types of Instructions:**
@@ -66,7 +66,9 @@ Let's customize Copilot's review considerations by adding custom instructions.
Let's create specific Copilot's review considerations for the frontend and backend.
-1. Create `.github/instructions/frontend.instructions.md` for and add the following content.
+1. Create `.github/instructions/frontend.instructions.md` and add the following guidelines.
+
+ > ❗️ **Important**: Make sure to put file-specific instructions in the `.github/instructions/` folder, not the `.github/` folder.
> ❗️ **Important**: Make sure to put file-specific instructions in the `.github/instructions/` folder, not the `.github/` folder.
@@ -82,7 +84,7 @@ Let's create specific Copilot's review considerations for the frontend and backe
- Validate HTML structure and semantic elements
```
-1. Create `.github/instructions/backend.instructions.md` for server-side guidelines:
+1. Create `.github/instructions/backend.instructions.md` and add the following guidelines:
```markdown
---
@@ -100,6 +102,9 @@ applyTo: "backend/**/*,*.py"
1. Commit and push the changes.
+> [!TIP]
+> VS Code has a built-in commands to help manage instructions. Try opening the command pallette and searching for `instructions`.
+
### ⌨️ Activity: Request another review
With our new instructions defined, Copilot now has a better idea of what is important for our project. Let's ask for another review.
@@ -108,6 +113,10 @@ With our new instructions defined, Copilot now has a better idea of what is impo
1. In the top right, find the **Reviewers** menu and **Re-request review** button next to **Copilot**. Click it and wait a moment for Copilot to add comments on the pull request.
+
+
+ > 🪧 **Note:** If you are too quick after pushing new commits, you may have to wait a moment for the button to appear.
+
1. Observe that Copilot's feedback now differs from the previous review.
1. With the review requested, wait a moment for Mona to check your work, provide feedback, and share the next lesson.
diff --git a/.github/steps/4-step.md b/.github/steps/4-step.md
index d159e10..41d7d0f 100644
--- a/.github/steps/4-step.md
+++ b/.github/steps/4-step.md
@@ -36,7 +36,7 @@ For more information, see the [repository rulesets documentation](https://docs.g
- **Ruleset Name**: `Require Copilot Reviews`
- **Enforcement Status**: `Active`
-1. Under **Target branches**, add protections for the `main` branch:
+1. Under **Target branches**, add protections for the `main` branch.
1. Click **Add target** and **Include default branch**.
1. Click **Add target** and **Include by pattern**.
@@ -62,19 +62,25 @@ For more information, see the [repository rulesets documentation](https://docs.g
1. Merge the pull request.
- > 💡 **Tip**: If the **Merge pull request** button doesn't activate, check for unresolved conversations in the outdated comments.
+ > 🪧 **Note**: If the **Merge pull request** button doesn't activate, check for unresolved conversations in the outdated comments.
-1. With the pull requested merged, wait a moment for Mona to check your work, provide feedback, and share the next lesson.
+1. With the pull request merged, wait a moment for Mona to check your work, provide feedback, and share the next lesson.
### ⌨️ Activity: (optional) Test required review
+Not ready to finish yet? Are you concerned by the hard coded announcement banner?
+
+Us too! So, let's fix it! 🧑🚀🚀
+
+1. In VS Code, switch back to the `main` branch, pull the merged changes, and delete the `add-announcement-banner` branch.
+
1. Create a new branch with the following name.
```txt
enable-editing-announcements
```
-1. Ask copilot to upgrade our new Announcements feature.
+1. Open the Copilot Chat panel and ask Copilot to upgrade our new Announcements feature.
> 
>
@@ -82,9 +88,12 @@ For more information, see the [repository rulesets documentation](https://docs.g
> The Announcement feature should not be hard coded.
>
> - Make it driven from the database.
- > - Allow signed in teachers to create and modify announcements.
+ > - Add a button in the header that opens a dialog window. It lists all existing announcements and has controls to add/modify/delete them.
+ > - Only signed in users have access to manage announcements.
> - Announcements require an expiration date. Start date is optional.
> - Add an example message to the database initialization.
+ > - Don't worry about unit testing.
+ > - Make it pretty with a good UI/UX experience.
> ```
1. (optional) Run the application to test the changes.
@@ -105,13 +114,4 @@ For more information, see the [repository rulesets documentation](https://docs.g
1. Merge the pull request.
-1. With the review requested, wait a moment for Mona to check your work, provide feedback, and share the next lesson.
-
-
+1. Nice work! You are all done, again! 🎉
diff --git a/.github/workflows/1-step.yml b/.github/workflows/1-step.yml
index 5490e28..cddb699 100644
--- a/.github/workflows/1-step.yml
+++ b/.github/workflows/1-step.yml
@@ -59,7 +59,7 @@ jobs:
continue-on-error: true
uses: skills/action-keyphrase-checker@v1.1.0
with:
- text-file: rc/static/index.html
+ text-file: src/static/index.html
keyphrase: announcement
- name: Check style.css for keyphrase
@@ -67,7 +67,7 @@ jobs:
continue-on-error: true
uses: skills/action-keyphrase-checker@v1.1.0
with:
- text-file: rc/static/index.html
+ text-file: src/static/style.css
keyphrase: announcement-banner
- name: Update comment - step results
@@ -79,7 +79,7 @@ jobs:
edit-mode: replace
file: exercise-toolkit/markdown-templates/step-feedback/step-results-table.md
vars: |
- step_number: 2
+ step_number: 1
results_table:
- description: "announcement added to index.html"
passed: ${{ steps.check-index-html.outcome == 'success' }}
diff --git a/src/backend/database.py b/src/backend/database.py
index 7c1d056..69d2f22 100644
--- a/src/backend/database.py
+++ b/src/backend/database.py
@@ -3,7 +3,7 @@
"""
from pymongo import MongoClient
-from argon2 import PasswordHasher
+from argon2 import PasswordHasher, exceptions as argon2_exceptions
# Connect to MongoDB
client = MongoClient('mongodb://localhost:27017/')
@@ -12,11 +12,30 @@
teachers_collection = db['teachers']
# Methods
+
+
def hash_password(password):
"""Hash password using Argon2"""
ph = PasswordHasher()
return ph.hash(password)
+
+def verify_password(hashed_password: str, plain_password: str) -> bool:
+ """Verify a plain password against an Argon2 hashed password.
+
+ Returns True when the password matches, False otherwise.
+ """
+ ph = PasswordHasher()
+ try:
+ ph.verify(hashed_password, plain_password)
+ return True
+ except argon2_exceptions.VerifyMismatchError:
+ return False
+ except Exception:
+ # For any other exception (e.g., invalid hash), treat as non-match
+ return False
+
+
def init_database():
"""Initialize database if empty"""
@@ -24,11 +43,13 @@ def init_database():
if activities_collection.count_documents({}) == 0:
for name, details in initial_activities.items():
activities_collection.insert_one({"_id": name, **details})
-
+
# Initialize teacher accounts if empty
if teachers_collection.count_documents({}) == 0:
for teacher in initial_teachers:
- teachers_collection.insert_one({"_id": teacher["username"], **teacher})
+ teachers_collection.insert_one(
+ {"_id": teacher["username"], **teacher})
+
# Initial database if empty
initial_activities = {
@@ -172,7 +193,7 @@ def init_database():
"display_name": "Ms. Rodriguez",
"password": hash_password("art123"),
"role": "teacher"
- },
+ },
{
"username": "mchen",
"display_name": "Mr. Chen",
@@ -186,4 +207,3 @@ def init_database():
"role": "admin"
}
]
-
diff --git a/src/backend/routers/auth.py b/src/backend/routers/auth.py
index 616917a..75a11ff 100644
--- a/src/backend/routers/auth.py
+++ b/src/backend/routers/auth.py
@@ -4,31 +4,26 @@
from fastapi import APIRouter, HTTPException
from typing import Dict, Any
-import hashlib
-from ..database import teachers_collection
+from ..database import teachers_collection, verify_password
router = APIRouter(
prefix="/auth",
tags=["auth"]
)
-def hash_password(password):
- """Hash password using SHA-256"""
- return hashlib.sha256(password.encode()).hexdigest()
@router.post("/login")
def login(username: str, password: str) -> Dict[str, Any]:
"""Login a teacher account"""
- # Hash the provided password
- hashed_password = hash_password(password)
-
# Find the teacher in the database
teacher = teachers_collection.find_one({"_id": username})
-
- if not teacher or teacher["password"] != hashed_password:
- raise HTTPException(status_code=401, detail="Invalid username or password")
-
+
+ # Verify password using Argon2 verifier from database.py
+ if not teacher or not verify_password(teacher.get("password", ""), password):
+ raise HTTPException(
+ status_code=401, detail="Invalid username or password")
+
# Return teacher information (excluding password)
return {
"username": teacher["username"],
@@ -36,16 +31,17 @@ def login(username: str, password: str) -> Dict[str, Any]:
"role": teacher["role"]
}
+
@router.get("/check-session")
def check_session(username: str) -> Dict[str, Any]:
"""Check if a session is valid by username"""
teacher = teachers_collection.find_one({"_id": username})
-
+
if not teacher:
raise HTTPException(status_code=404, detail="Teacher not found")
-
+
return {
"username": teacher["username"],
"display_name": teacher["display_name"],
"role": teacher["role"]
- }
\ No newline at end of file
+ }