Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Autograder

## Big Picture ##
## Big Picture

1. Front end is written in React, which is a Typescript framework. Vite is the front-end build tool.
2. Database will be Postgres. Database migrations are managed using Goose.
3. Backend is Go with the Echo framework.
## How to setup the development environment

## How to setup the development environment

### On Linux/Mac

Expand All @@ -14,21 +15,25 @@
3. On the terminal, run `chmod +x ./dev.sh` to modify permissions
4. On the terminal, run `cd web && yarn && yarn run dev` to install the React dependencies. Once you see the `build in <time>ms` message, you can exit out of the command using `Ctrl+C`
5. Add the following to `~/.bashrc` on Linux or `~/.zshrc` on Mac.

```
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
```

Then, run `source ~/.bashrc` on Linux or `source ~/.zshrc` on Mac.

6. Install `air` (live-reload for go applications) and `goose` (database migration tool) by running

```
go install github.com/air-verse/air@latest
go install github.com/pressly/goose/v3/cmd/goose@latest
```

7. [Install postgres](#how-to-install-postgres) and create a database named `autograder`. Make sure you postgres is up and running. You can download [Beekeeper Studio](https://www.beekeeperstudio.io/get-community) as a database explorer if you don't already have one.
8. Run `source .envrc` to export the variables of `.envrc` on your local machine. Run `goose up` to run all the required database migrations.
8. Run `source .envrc` to export the variables of `.envrc` on your local machine. Run `goose up` to run all the required database migrations.
9. Open http://localhost:8080/ to access the web app.

### On Windows w/o WSL
Expand All @@ -54,13 +59,15 @@ go install github.com/pressly/goose/v3/cmd/goose@latest
## How to install postgres

### Mac (option 1)

1. Download the most recent version of postgres via their [installer](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads) under the column "Mac OS X"
2. Go through the steps in the installer. When it prompts you to set up your database admin make the username `postgres` and password `postgres`.
3. After the install is finished, open pgAdmin which will prompt you to type in your password (`postgres`) to connect to your local server that's been setup via the installation.
4. On the left column under the dropdown menu for `PosgreSQL [Version #]` right click `Databases` and click `Create` > `Database...`.
5. Name it `autograder` and click Save.

### Mac (option 2)

1. Install postgres using `brew install postgresql`.
2. Start postgres using `brew services start postgresql`.
3. Run `psql postgres`.
Expand All @@ -71,24 +78,28 @@ go install github.com/pressly/goose/v3/cmd/goose@latest
## How to build the binary

**Note:** Before building the binary for deployment, please do the following:
1. Set up a Postgres database. Update the credentials in `config.yaml` with the database's credentials.
2. Chanage the default JWT Secret in `config.yaml` to be a long and secure sequence of charaters.

1. Set up a Postgres database. Update the credentials in `config.yaml` with the database's credentials.
2. Chanage the default JWT Secret in `config.yaml` to be a long and secure sequence of charaters.
3. TODO (Setting up Email stuff)

### For Linux / Mac
Run `go build main.go` on the terminal. This will create a new binary file.

Run `go build main.go` on the terminal. This will create a new binary file.

You can now run the binary using `./main` on the terminal.

### For Windows

Run `env GOOS=windows GOARCH=arm64 go build main.go` on the terminal.

If the above doesn't generate a binary or generates a binary that doesn't run on your computer, change the `GOARCH` value from `arm64` to the correct one for your CPU. You can find the list for valid `GOARCH` values [here](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63#goarch-values).

You can now run the using `main.exe`

## The Three-Layered Architecture
The backend for this project makes use of a slightly modified version of the Three-Layered Architecture. The incoming requests are parsed into the correspoding entity (defined inside `models`) by the `handler` layer. A valid request passes from the `handler` into the `service` layer, where all the business logic is performed. The next step after the `service` layer is the `datastore` layer, where the necessary database operation is performed.

The backend for this project makes use of a slightly modified version of the Three-Layered Architecture. The incoming requests are parsed into the correspoding entity (defined inside `models`) by the `handler` layer. A valid request passes from the `handler` into the `service` layer, where all the business logic is performed. The next step after the `service` layer is the `datastore` layer, where the necessary database operation is performed.

Such an architecture allows one layer to be modified and tested independently of other operations in addition to providing all the benefits of modularity.

Expand All @@ -97,91 +108,104 @@ Such an architecture allows one layer to be modified and tested independently of
You can use Postman to test the APIs.

Register:

```
POST
POST
URL: 'http://localhost:8080/api/auth/register/<id>?token=<token>'
Raw JSON: {"first_name": "New", "last_name": "User", "password": "Hello123$%"}
```

From the returned Set-Cookie value, take the token's value and set it as the Bearer Token value inside Authorization.
From the returned Set-Cookie value, take the token's value and set it as the Bearer Token value inside Authorization.

Create Invite (need token):

```
POST
URL: 'http://localhost:8080/api/auth/invite'
Raw JSON: {"email": "testing@gmail.com", "user_role": "student"}
```

Create Classroom (need token):

```
POST
POST
URL: 'http://localhost:8080/api/classroom'
Raw JSON: {"name":"Joe"}
```

Add user to classroom / alter the roles of existing users in classroom:

```
PUT
URL: http://localhost:8080/api/auth/<classroomId>/user
Raw JSON:
Raw JSON:
{
"users": [{"email": "test@udallas.edu", "role": "student"}]
}
```

Change the name of a classroom:

```
PATCH
URL: http://localhost:8080/api/classroom/edit/<classroomId>
URL: http://localhost:8080/api/classroom/<classroomId>
Raw JSON: {"name": "New Name"}
```

Delete a classroom:

```
DELETE
URL: http://localhost:8080/api/classroom/delete/<classroomId>
URL: http://localhost:8080/api/classroom/<classroomId>
```

Get 'view mode' assignments

```
GET
URL: http://localhost:8080/api/classroom/<classroomId>/view_assignments
```

Login:

```
POST
POST
URL: 'http://localhost:8080/api/auth/login'
Raw JSON: {"email":"test@udallas.edu", "password":"Hello123$%"}
```

Logout:

```
POST
URL: 'http://localhost:8080/api/auth/logout/<sessionId>'
```

Password Reset Request:

```
POST
URL: 'http://localhost:8080/api/auth/password'
Raw JSON: {"email":"test@udallas.edu"}
```

Password Reset:

```
POST
URL: 'http://localhost:8080/api/auth/reset_password/<requestId>?token=<token>'
Raw JSON: {"password":"TryingThis123!"}
```

Refresh Access Token:

```
POST
URL: 'http://localhost:8080/api/auth/refresh'
```

Change User Info:

```
PUT
URL: 'http://localhost:8080/api/auth/user_info
Expand All @@ -194,12 +218,16 @@ Raw JSON:
}

```

Is Login Valid:

```
GET
URL: 'http://localhost:8080/api/auth/valid_login
```

Get user's first and last name:

```
GET
URL: 'http://localhost:8080/api/auth/user_name'
Expand Down
4 changes: 2 additions & 2 deletions handler/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ func (router *HttpRouter) SetupRoutes() {
classroom.GET("/all", router.GetClassroomsOfUser)
classroom.POST("", router.CreateClassroom)
classroom.GET("/:room_id", router.GetClassroom)
classroom.PATCH("/edit/:room_id", router.EditClassroom)
classroom.DELETE("/delete/:room_id", router.DeleteClassroom)
classroom.PATCH("/:room_id", router.EditClassroom)
classroom.DELETE("/:room_id", router.DeleteClassroom)
classroom.GET("/:room_id/view_assignments", router.GetViewAssignments)
classroom.GET("/:room_id/verbose_assignments", router.GetVerboseAssignments)
classroom.POST("/:room_id/verbose_assignments", router.SetVerboseAssignments)
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"react-router-dom": "^7.9.1"
}
}
8 changes: 3 additions & 5 deletions web/src/index.html → web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="stylesheet" href="./index.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@100..900&display=swap" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Monomaniac One' rel='stylesheet'>
<link href="./reset.css" rel="stylesheet">
<title>UDCS Autograder</title>
<title>Autograder</title>
</head>
<body>
<div id="root">
<script type="module" src="./Index.tsx"></script>
<script type="module" src="./src/main.tsx"></script>
</div>
</body>
</html>
5 changes: 3 additions & 2 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"@monaco-editor/react": "^4.7.0",
"@tailwindcss/vite": "^4.0.3",
"clsx": "^2.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.9.1",
"react-select": "^5.10.0",
"react-switch": "^7.1.0",
"styled-components": "^6.1.14",
Expand Down
35 changes: 35 additions & 0 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Routes, Route, Navigate } from "react-router";
import Home from "./home/Home";
import NavBar from "./components/navbar/Navbar";
import Login from "./login/Login";
import Signup from "./signup/Signup";
import About from "./about/About";
import Assignment from "./assignment/Assignment";
import Account from "./account/AccountSettings";
import Classroom from "./classroom/Classroom";
import Dashboard from "./dashboard/Dashboard";
import Faq from "./faq/FAQ";
import ResetPassword from "./resetpassword/Resetpassword";

function App() {
return (
<div className="app">
<NavBar />
<Routes>
<Route path="/i" element={<Home />} />
<Route path="/i/about" element={<About />} />
<Route path="/i/account" element={<Account />} />
<Route path="/i/assignment" element={<Assignment />} />
<Route path="/i/classroom" element={<Classroom />} />
<Route path="/i/dashboard" element={<Dashboard />} />
<Route path="/i/faq" element={<Faq />} />
<Route path="/i/login" element={<Login />} />
<Route path="/i/reset" element={<ResetPassword />} />
<Route path="/i/signup" element={<Signup />} />
<Route path="*" element={<Navigate to="/i" />} />
</Routes>
</div>
);
}

export default App;
13 changes: 0 additions & 13 deletions web/src/FAQ/FAQ.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
@import url(..//reset.css);
@import "tailwindcss";

* {
overscroll-behavior: none;
}

body {
font-family: "Azeret Mono";
margin: 0;
overflow: hidden;
}

#notication-text {
font-family: "Monomaniac One";
font-size: 96px;
Expand Down
19 changes: 0 additions & 19 deletions web/src/FAQ/FAQ.html

This file was deleted.

24 changes: 11 additions & 13 deletions web/src/FAQ/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import Navbar from "../components/navbar/Navbar"
import './FAQ.css'
import "./FAQ.css";

createRoot(document.getElementById('root')!).render(
<StrictMode>
<Navbar />
<div className="bottom-page">
<h1 id="notication-text">Page under development</h1>
<a href="/test" id="page">Test page</a>
</div>
</StrictMode>,
)
function Faq() {
return (
<>
<h1 id="notication-text">Page under development</h1>
Test page
</>
);
}

export default Faq;
18 changes: 0 additions & 18 deletions web/src/Index.tsx

This file was deleted.

Loading
Loading