Merge branch 'web' into scheduler

This commit is contained in:
Square Root 2021-02-17 11:18:19 +05:30
commit a552cff572
13 changed files with 385 additions and 100 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.key
*.sqlite
**/config.json
.vim/

View File

@ -2,7 +2,11 @@
Mark your attendance in eduserver automatically.
## Running the web server
## Running
This project has two parts the web server which gives the UI to enter new users and the scheduler which does all the heavy lifting and marks the attendance.
### Web server
Requires
- php >= 7
@ -10,8 +14,24 @@ Requires
- curl
- php-curl
- web server (only needed in production)
- mysql/postgres (recommended)
In production hide the folder `/php` from the webserver. For development use,
In production hide the folder `php` from the webserver. For development use,
```sh
php -S localhost:8001 -t web
```
php -s localhost:8001 -t web
```
All is well.
#### More configuration
The default database is `sqlite`. If you are using any other database engine, specify the configuration in `web/php/config.json`. For a starting reference see `web/php/config.sample.json`.
```json
{
"DBPATH": "mysql:host=localhost;dbname=myDb",
"DBUSERNAME": "user",
"DBPASSWORD": "pass"
}
```
The configuration can also be read from enviroment variables but the `config.json` is given precedence.

View File

@ -18,8 +18,10 @@
html_head("About");
?>
<body>
<?php require(__DIR__."/php/components/header.php"); ?>
<?php require(__DIR__."/php/components/footer.php"); ?>
<div class="container">
<?php require(__DIR__."/php/components/header.php"); ?>
<?php require(__DIR__."/php/components/footer.php"); ?>
</div>
</body>
</html>

View File

@ -0,0 +1,5 @@
<svg width="199" height="65" viewBox="0 0 199 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0H61C95.7939 0 124 28.2061 124 63H63C28.2061 63 0 34.7939 0 0Z" fill="#3BD671"/>
<path d="M11.0781 40.9766V53H4.04688V18.875H17.3594C19.9219 18.875 22.1719 19.3438 24.1094 20.2812C26.0625 21.2188 27.5625 22.5547 28.6094 24.2891C29.6562 26.0078 30.1797 27.9688 30.1797 30.1719C30.1797 33.5156 29.0312 36.1562 26.7344 38.0938C24.4531 40.0156 21.2891 40.9766 17.2422 40.9766H11.0781ZM11.0781 35.2812H17.3594C19.2188 35.2812 20.6328 34.8438 21.6016 33.9688C22.5859 33.0938 23.0781 31.8438 23.0781 30.2188C23.0781 28.5469 22.5859 27.1953 21.6016 26.1641C20.6172 25.1328 19.2578 24.6016 17.5234 24.5703H11.0781V35.2812ZM48.8359 33.9922C47.9141 33.8672 47.1016 33.8047 46.3984 33.8047C43.8359 33.8047 42.1562 34.6719 41.3594 36.4062V53H34.5859V27.6406H40.9844L41.1719 30.6641C42.5312 28.3359 44.4141 27.1719 46.8203 27.1719C47.5703 27.1719 48.2734 27.2734 48.9297 27.4766L48.8359 33.9922ZM50.2188 40.0859C50.2188 37.5703 50.7031 35.3281 51.6719 33.3594C52.6406 31.3906 54.0312 29.8672 55.8438 28.7891C57.6719 27.7109 59.7891 27.1719 62.1953 27.1719C65.6172 27.1719 68.4062 28.2188 70.5625 30.3125C72.7344 32.4062 73.9453 35.25 74.1953 38.8438L74.2422 40.5781C74.2422 44.4688 73.1562 47.5938 70.9844 49.9531C68.8125 52.2969 65.8984 53.4688 62.2422 53.4688C58.5859 53.4688 55.6641 52.2969 53.4766 49.9531C51.3047 47.6094 50.2188 44.4219 50.2188 40.3906V40.0859ZM56.9922 40.5781C56.9922 42.9844 57.4453 44.8281 58.3516 46.1094C59.2578 47.375 60.5547 48.0078 62.2422 48.0078C63.8828 48.0078 65.1641 47.3828 66.0859 46.1328C67.0078 44.8672 67.4688 42.8516 67.4688 40.0859C67.4688 37.7266 67.0078 35.8984 66.0859 34.6016C65.1641 33.3047 63.8672 32.6562 62.1953 32.6562C60.5391 32.6562 59.2578 33.3047 58.3516 34.6016C57.4453 35.8828 56.9922 37.875 56.9922 40.5781ZM87.5547 35.2109L91.8203 27.6406H99.0625L91.8438 40.0625L99.3672 53H92.1016L87.5781 45.0312L83.0781 53H75.7891L83.3125 40.0625L76.1172 27.6406H83.3828L87.5547 35.2109ZM111.789 43.4141L116.477 27.6406H123.742L113.547 56.9375L112.984 58.2734C111.469 61.5859 108.969 63.2422 105.484 63.2422C104.5 63.2422 103.5 63.0938 102.484 62.7969V57.6641L103.516 57.6875C104.797 57.6875 105.75 57.4922 106.375 57.1016C107.016 56.7109 107.516 56.0625 107.875 55.1562L108.672 53.0703L99.7891 27.6406H107.078L111.789 43.4141Z" fill="white"/>
<path d="M126.859 53V18.875H138.812C142.953 18.875 146.094 19.6719 148.234 21.2656C150.375 22.8438 151.445 25.1641 151.445 28.2266C151.445 29.8984 151.016 31.375 150.156 32.6562C149.297 33.9219 148.102 34.8516 146.57 35.4453C148.32 35.8828 149.695 36.7656 150.695 38.0938C151.711 39.4219 152.219 41.0469 152.219 42.9688C152.219 46.25 151.172 48.7344 149.078 50.4219C146.984 52.1094 144 52.9688 140.125 53H126.859ZM133.891 38.1406V47.3516H139.914C141.57 47.3516 142.859 46.9609 143.781 46.1797C144.719 45.3828 145.188 44.2891 145.188 42.8984C145.188 39.7734 143.57 38.1875 140.336 38.1406H133.891ZM133.891 33.1719H139.094C142.641 33.1094 144.414 31.6953 144.414 28.9297C144.414 27.3828 143.961 26.2734 143.055 25.6016C142.164 24.9141 140.75 24.5703 138.812 24.5703H133.891V33.1719ZM171.32 33.9922C170.398 33.8672 169.586 33.8047 168.883 33.8047C166.32 33.8047 164.641 34.6719 163.844 36.4062V53H157.07V27.6406H163.469L163.656 30.6641C165.016 28.3359 166.898 27.1719 169.305 27.1719C170.055 27.1719 170.758 27.2734 171.414 27.4766L171.32 33.9922ZM172.703 40.0859C172.703 37.5703 173.188 35.3281 174.156 33.3594C175.125 31.3906 176.516 29.8672 178.328 28.7891C180.156 27.7109 182.273 27.1719 184.68 27.1719C188.102 27.1719 190.891 28.2188 193.047 30.3125C195.219 32.4062 196.43 35.25 196.68 38.8438L196.727 40.5781C196.727 44.4688 195.641 47.5938 193.469 49.9531C191.297 52.2969 188.383 53.4688 184.727 53.4688C181.07 53.4688 178.148 52.2969 175.961 49.9531C173.789 47.6094 172.703 44.4219 172.703 40.3906V40.0859ZM179.477 40.5781C179.477 42.9844 179.93 44.8281 180.836 46.1094C181.742 47.375 183.039 48.0078 184.727 48.0078C186.367 48.0078 187.648 47.3828 188.57 46.1328C189.492 44.8672 189.953 42.8516 189.953 40.0859C189.953 37.7266 189.492 35.8984 188.57 34.6016C187.648 33.3047 186.352 32.6562 184.68 32.6562C183.023 32.6562 181.742 33.3047 180.836 34.6016C179.93 35.8828 179.477 37.875 179.477 40.5781Z" fill="#3BD671"/>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -20,23 +20,20 @@
html_head("Dashboard");
?>
<body>
<header>
<div class="container">
<?php require(__DIR__."/php/components/header.php"); ?>
</header>
<?php
if ($user->active) {
echo '<a href="/toggle_activation.php">Deactivate account</a>';
echo "Account active";
} else {
echo '<a href="/toggle_activation.php">Activate account</a>';
echo "Account inactive";
}
?>
<?php require(__DIR__."/php/components/header.php"); ?>
<?php
if ($user->active) {
echo '<a href="/toggle_activation.php">Deactivate account</a>';
echo "Account active";
} else {
echo '<a href="/toggle_activation.php">Activate account</a>';
echo "Account inactive";
}
?>
<?php require(__DIR__."/php/components/footer.php"); ?>
<?php require(__DIR__."/php/components/footer.php"); ?>
</div>
</body>
</html>

View File

@ -12,46 +12,40 @@
$num_users = $db->getTotalCount("users");
$error_login = $error_signup = $login_username = $signup_username = "";
$error = $username = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (isset($_POST["login"])) {
$user = new User();
$login_username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";
$user = new User();
$username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";
try {
$user->find($db, $username);
try {
$user->login($db, $login_username, $password);
$user->login($db, $username, $password);
header("location: /dashboard.php");
} catch (Exception $e) {
$error_login = $e->getMessage();
$error = $e->getMessage();
}
} else if (isset($_POST["signup"])) {
$user = new User();
$signup_username = $_POST["username"] ?? "";
$password = $_POST["password"] ?? "";
$user->username = $signup_username;
$user->password = $password;
} catch (Exception $e) {
try {
$user->username = $username;
$user->password = $password;
$user->save($db);
$_SESSION["username"] = $signup_username;
$_SESSION["username"] = $username;
header("location: /dashboard.php");
} catch (Exception $e) {
$error_signup = $e->getMessage();
$error = $e->getMessage();
}
}
}
}
$isSignupError = ($error_signup !== "");
$isLoginError = ($error_login !== "");
$isError = ($error !== "");
?>
@ -61,49 +55,35 @@
?>
<body>
<?php require(__DIR__."/php/components/header.php"); ?>
<div class="container">
<?php require(__DIR__."/php/components/header.php"); ?>
<main>
<p>Tired of marking your attendance in eduserver? Don't worry we got your back. Just sign up here with your eduserver credentials and we will mark the attendance for you so you can enjoy the <b><em>classes</em></b> without bothering about attendance. </p>
<p>Current users <?php echo $num_users; ?> </p>
</main>
<section id="signup">
<h2>Signup</h2>
<form id="signup-form" method="post" action="/index.php">
<div>
<label for="signup-username">Username</label>
<input id="signup-username" type="text" name="username" value="<?php echo $signup_username; ?>"placeholder="Your eduserver username" />
</div>
<div>
<label for="signup-password">Password</label>
<input id="signup-password" type="password" name="password" placeholder="Your eduserver password" />
</div>
<span>Please not that we will be storing this password in a database as plain text. So please don't use your master or important passwords.</span>
<div>
<div><?php echo $error_signup; ?></div>
<button name="signup" type="submit">Signup</button>
</div>
</form>
</section>
<section id="login">
<h2>Login</h2>
<form id="login-form" method="post" action="/index.php">
<div>
<label for="login-username">Username</label>
<input id="login-username" type="text" name="username" value="<?php echo $login_username; ?>" placeholder="Your username" />
</div>
<div>
<label for="login-password">Password</label>
<input id="login-password" type="password" name="password" placeholder="Your password" />
</div>
<div><?php echo $error_login; ?></div>
<span>Use the credentials used for signup</span>
<div>
<button name="login" type="submit">Login</button>
</div>
</form>
</section>
<main id="homepage">
<section class="part left">
<p class="site-message">Tired of marking your attendance in eduserver? Don't worry we got your back. Just sign up here with your eduserver credentials and we will mark the attendance for you so you can enjoy the <span class="em">classes</span> without bothering about attendance. </p>
<?php require(__DIR__."/php/components/footer.php"); ?>
<div class="message-card">
<p>Current users <span class="accent"><?php echo $num_users; ?></span> </p>
</div>
</section>
<section class="part right" id="signup">
<div>
<h2 class="heading">Signin or Signup</h2>
<span class="sub">Please note that we will be storing this password in a database as plain text. So please don't use your master or important passwords.</span>
</div>
<form id="form" method="post" action="/index.php">
<input class="input <?php echo $isError ? "error" : ""; ?>" id="username" type="text" name="username" value="<?php echo $username; ?>"placeholder="eduserver username" />
<input class="input <?php echo $isError ? "error" : ""; ?>" id="password" type="password" name="password" placeholder="eduserver password" />
<div class="error"><?php echo $error; ?></div>
<div class="bottom-container">
<button type="submit" class="button accent">Enter</button>
</div>
</form>
</section>
</main>
<?php require(__DIR__."/php/components/footer.php"); ?>
</div>
</body>
</html>
</html>

23
web/php/Config.php Normal file
View File

@ -0,0 +1,23 @@
<?php
class Config {
function __construct($path = __DIR__ . "/config.json") {
$this->getConfigFromEnv = false;
if (file_exists($path)) {
$keys = json_decode(file_get_contents($path), true);
if (!is_null($keys)) {
foreach ($keys as $key => $value) {
putenv("{$key}=${value}");
}
}
}
}
public function __get($property) {
if (getenv($property) !== false) {
return getenv($property);
} else {
return null;
}
}
}

View File

@ -1,11 +1,18 @@
<?php
class Database {
function __construct($path = "sqlite:" . __DIR__ . "/../../users.sqlite") {
function __construct($path = null) {
if (is_null($path)) {
$path = "sqlite:" . __DIR__ . "/../../users.sqlite";
}
$this->path = $path;
}
function connect() {
$this->dbh = new PDO($this->path) or die("cannot open the database");
function connect($username = null, $password=null) {
if (!is_null($username) && !is_null($password)) {
$this->dbh = new PDO($this->path, $username, $password);
} else {
$this->dbh = new PDO($this->path) or die("cannot open the database");
}
return $this->dbh;
}

View File

@ -1,4 +1,4 @@
<header>
<h1>ProxyBro</h1>
<a href="/"><img class="brand-name" src="/assets/brand-name.svg" /></a>
<?php require(__DIR__."/nav.php"); ?>
</header>

View File

@ -3,7 +3,6 @@
echo '
<nav>
<a href="/dashboard.php">Dashboard</a>
<a href="/about.php">About</a>
<a href="/logout.php">Logout</a>
<a href="https://git.disroot.org/sQr00t/ProxyBro" target="_blank" rel="noopener noreferrer">Contribute</a>
</nav>';
@ -11,7 +10,6 @@
echo '
<nav>
<a href="/">Home</a>
<a href="/about.php">About</a>
<a href="https://git.disroot.org/sQr00t/ProxyBro" target="_blank" rel="noopener noreferrer">Contribute</a>
</nav>';
}

View File

@ -0,0 +1,5 @@
{
"DBPATH": "mysql:host=localhost;dbname=myDb",
"DBUSERNAME": "user",
"DBPASSWORD": "pass"
}

View File

@ -3,6 +3,7 @@
function add_dependancies() {
require_once(__DIR__ ."/Database.php");
require_once(__DIR__ ."/User.php");
require_once(__DIR__ ."/Config.php");
}
function redirect_authenticated() {
@ -34,8 +35,14 @@ function get_user_from_session($db, $show_error = true) {
function connect_db() {
try {
$db = new Database();
$db->connect();
$config = new Config();
$path = $config->DBPATH;
$username = $config->DBUSERNAME;
$password = $config->DBPASSWORD;
$db = new Database($path);
$db->connect($username, $password);
$db->create_tables();
return $db;
} catch (Exception $e) {
@ -51,8 +58,6 @@ function eduserver_login($username, $password) {
$page_data = send_get($login_url);
$login_token = get_data_between($page_data, 'name="logintoken" value="', '"');
echo $username . $password . $login_token;
$login_data = send_post($login_url, [
"logintoken" => $login_token,

View File

@ -0,0 +1,242 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
*:focus {
outline: none;
border: 1px solid var(--color-off-white);
border-radius: 5px;
}
:root {
--color-primary: #131a26;
--color-primary-dark: #0c1018;
--color-accent: #3bd671;
--color-accent-dark: #199b46;
--color-accent-error: #e41919;
--color-accent-error-border: #9b2222;
--color-primary-light: #212937;
--color-body: #687790;
--color-heading: #ffffff;
--color-off-white: #455775;
--color-input: #323b49;
--color-input-placeholder: #525f72;
font-size: 16px;
font-family: "Roboto", sans-serif;
font-weight: 400;
--font-normal: 1rem;
--font-medium: 1.2rem;
--font-large: 1.7rem;
--font-xl: 2.2rem;
}
body {
background-color: var(--color-primary);
color: var(--color-body);
font-size: var(--font-normal);
}
.container {
position: relative;
width: 100%;
margin: auto;
max-width: 1500px;
min-height: calc(100vh - 100px);
padding: 0 50px;
}
.heading {
color: var(--color-heading);
}
.em {
color: var(--color-heading);
font-style: italic;
font-weight: 500;
}
.accent {
color: var(--color-accent);
}
h2,
.h2 {
font-size: var(--font-xl);
font-weight: 700;
}
header {
width: 100%;
margin: auto;
max-width: 1400px;
display: flex;
flex-direction: column;
align-items: center;
}
header .brand-name {
width: 150px;
margin: 20px 0;
}
header nav a {
text-decoration: none;
padding: 5px 15px;
font-size: var(--font-medium);
color: var(--color-body);
transition: color 300ms ease-in-out;
}
header nav a:hover {
color: var(--color-heading);
}
/* footer */
footer {
position: absolute;
display: flex;
justify-content: space-between;
align-items: center;
left: 0;
bottom: -100px;
padding: 0 50px;
height: 100px;
width: 100%;
}
footer h4 {
font-size: var(--font-medium);
}
footer nav a {
text-decoration: none;
padding: 5px 15px;
color: var(--color-body);
transition: color 300ms ease-in-out;
}
footer nav a:hover {
color: var(--color-heading);
}
main#homepage {
padding-top: 75px;
}
main#homepage .part.left {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
border-radius: 10px;
text-align: center;
font-size: var(--font-medium);
line-height: 30px;
}
main#homepage .part.left .message-card {
margin-top: 80px;
margin-bottom: 80px;
background-color: var(--color-primary-light);
padding: 50px 70px;
border-radius: 5px;
font-size: var(--font-large);
color: var(--color-heading);
font-weight: 700;
}
main#homepage .part.right {
display: flex;
flex-direction: column;
background-color: var(--color-primary-light);
padding: 50px 70px;
border-radius: 5px;
}
main#homepage .part.right .sub {
display: block;
margin: 10px 0 30px 0;
}
main#homepage .part.right #form {
width: 100%;
display: flex;
flex-direction: column;
}
.input {
width: 100%;
background-color: var(--color-input);
color: var(--color-heading);
border: none;
padding: 15px;
margin: 10px 0;
font-size: var(--font-medium);
border-radius: 5px;
}
.input:focus, .button:focus {
border: inherit;
border: 1px solid var(--color-off-white);
}
.input.error {
color: var(--color-heading);
border: 2px solid var(--color-accent-error-border);
}
.input::placeholder {
color: var(--color-input-placeholder);
}
.button {
padding: 10px 25px;
font-size: var(--font-medium);
border: none;
border-radius: 5px;
font-weight: 500;
cursor: pointer;
}
.error {
color: var(--color-accent-error);
}
.button.accent {
background-color: var(--color-accent);
color: var(--color-heading);
transition: background-color 200ms ease-in-out;
}
.button.accent:hover {
background-color: var(--color-accent-dark);
}
form .bottom-container {
display: flex;
justify-content: flex-end;
padding-top: 20px;
}
@media screen and (min-width: 1000px) {
header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 50px 10px 50px 10px;
}
main#homepage {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: 50px;
padding-top: 7%;
}
}