Initial commit - signing up + boilerplate/schema
This commit is contained in:
commit
d8f1874ebb
10 changed files with 393 additions and 0 deletions
BIN
assets/favicon.png
Normal file
BIN
assets/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 404 KiB |
2
boilerplate/foot.php
Normal file
2
boilerplate/foot.php
Normal file
|
@ -0,0 +1,2 @@
|
|||
</body>
|
||||
</html>
|
34
boilerplate/head.php
Normal file
34
boilerplate/head.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title><?php
|
||||
if ($title) {
|
||||
echo "$title | ";
|
||||
}
|
||||
echo $site_name;
|
||||
?></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="theme-color" content="#190b14">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<link rel="apple-touch-icon" href="/assets/favicon.png">
|
||||
<link rel="icon" href="/assets/favicon.png">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php
|
||||
if ($title) {
|
||||
echo "<h1>$title</h1><h2>$site_name</h2>";
|
||||
} else {
|
||||
echo "<h1>$site_name</h1>";
|
||||
}
|
||||
|
||||
if ($err) {
|
||||
echo "<div class=\"err\">$err<div></div></div>";
|
||||
}
|
||||
?>
|
||||
|
36
config.php
Normal file
36
config.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
// site config
|
||||
$site_name = "Todo List Manager";
|
||||
$signups = "invite"; // true, false, "invite" - treats it as true if no users currently exist, invite requires an admin to invite the user
|
||||
|
||||
$admin_users = array(
|
||||
// "username1", "username2", ...
|
||||
);
|
||||
|
||||
// database config
|
||||
$db_host = "localhost";
|
||||
$db_name = "todo";
|
||||
$db_username = "postgres";
|
||||
$db_password = "postgres";
|
||||
|
||||
// DO NOT CHANGE THIS!!!
|
||||
$db_info = "host=$db_host dbname=$db_name user=$db_username password=$db_password";
|
||||
$db = pg_connect($db_info);
|
||||
|
||||
// default schema
|
||||
// DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!!!
|
||||
$default_schema = array(
|
||||
"general" => array(
|
||||
"name" => "General",
|
||||
"show_subtitle" => false,
|
||||
"items" => array(
|
||||
"general" => array(
|
||||
"name" => "",
|
||||
"display_format" => "For <b>%d</b> - %c"
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
79
css/base.css
Normal file
79
css/base.css
Normal file
|
@ -0,0 +1,79 @@
|
|||
:root {
|
||||
--background: #190b14;
|
||||
--text: #c5b8ca;
|
||||
--subtext: #c5a8ca80;
|
||||
--border: #d8a4c62a;
|
||||
--input-background: #2e1425;
|
||||
--button-background: #3c1a30;
|
||||
--button-hover-background: #5e2a4e;
|
||||
--accent: #d8a4c6;
|
||||
--red: #d67677;
|
||||
--yellow: #d3d381;
|
||||
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--accent);
|
||||
color: var(--background);
|
||||
}
|
||||
|
||||
body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--background);
|
||||
color: var(--text);
|
||||
min-height: calc(100vh - 16px);
|
||||
width: calc(100vw - 16px);
|
||||
overflow-x: hidden;
|
||||
margin: 8px;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: var(--input-background);
|
||||
color: var(--color);
|
||||
border: 1px solid var(--border);
|
||||
padding: 3px 5px;
|
||||
border-radius: 7.5px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: 2px solid var(--subtext);
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: var(--subtext);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
i {
|
||||
color: var(--subtext);
|
||||
}
|
||||
|
||||
.err {
|
||||
color: var(--red);
|
||||
border: 2px dashed var(--red);
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#container {
|
||||
text-align: left;
|
||||
margin: 0 10vw;
|
||||
}
|
37
helper.php
Normal file
37
helper.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
function generate_id(): string {
|
||||
return hash("sha256", uniqid("", true));
|
||||
}
|
||||
|
||||
function get_token(string $username, string $password_hash): string {
|
||||
return $username . "-" . hash("sha256", $password_hash . $username);
|
||||
}
|
||||
|
||||
function validate_token(string $token): bool {
|
||||
}
|
||||
|
||||
function is_logged_in(): false | array {
|
||||
$token = $_COOKIE["token"];
|
||||
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $db;
|
||||
|
||||
$u = explode("-", $token, 2)[0];
|
||||
$user_object = pg_select(
|
||||
$db, "users", array(
|
||||
"username" => $u
|
||||
)
|
||||
);
|
||||
|
||||
if ($user_object && get_token($user_object[0]["username"], $user_object[0]["password_hash"]) === $token) {
|
||||
return $user_object;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
?>
|
16
index.php
Normal file
16
index.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
include "config.php";
|
||||
include "boilerplate/head.php";
|
||||
?>
|
||||
|
||||
<a href="login.php">Log In</a>
|
||||
|
||||
<?php
|
||||
$q = "SELECT count(*) FROM users LIMIT 1;";
|
||||
|
||||
if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) {
|
||||
echo "- <a href=\"signup.php\">Sign Up</a>";
|
||||
}
|
||||
|
||||
include "boilerplate/foot.php";
|
||||
?>
|
46
login.php
Normal file
46
login.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
include "config.php";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$u = $_POST["username"];
|
||||
$p = $_POST["password"];
|
||||
|
||||
if ($u && $p) {
|
||||
$response = pg_select(
|
||||
$db,
|
||||
"users",
|
||||
array("username" => $u)
|
||||
);
|
||||
|
||||
if (sizeof($response) === 0) {
|
||||
$err = "User '" . htmlspecialchars($u) . "' not found";
|
||||
} else {
|
||||
echo json_encode($response);
|
||||
}
|
||||
} else {
|
||||
$err = "Bad request, missing username or password parameter";
|
||||
}
|
||||
} else {
|
||||
$u = "";
|
||||
$p = "";
|
||||
}
|
||||
|
||||
$title = "Log In";
|
||||
include "boilerplate/head.php";
|
||||
?>
|
||||
|
||||
<form method="POST">
|
||||
<div><input placeholder="Username" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div>
|
||||
<div><input placeholder="Password" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div>
|
||||
<div><input type="submit" value="Log in"></div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
$q = "SELECT count(*) FROM users LIMIT 1;";
|
||||
|
||||
if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) {
|
||||
echo "<p><a href=\"signup.php\">Sign up instead?</a></p>";
|
||||
}
|
||||
|
||||
include "boilerplate/foot.php";
|
||||
?>
|
43
setup.php
Normal file
43
setup.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
include "config.php";
|
||||
|
||||
$queries = array(
|
||||
"CREATE TABLE IF NOT EXISTS users (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
username VARCHAR(64) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(256) NOT NULL
|
||||
);",
|
||||
"CREATE TABLE IF NOT EXISTS section (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
users VARCHAR(64) NOT NULL REFERENCES users(id),
|
||||
name VARCHAR(128),
|
||||
show_subtitle BOOLEAN NOT NULL -- whether or not to separately show the name of the first item
|
||||
);",
|
||||
"CREATE TABLE IF NOT EXISTS row (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
section VARCHAR(64) NOT NULL REFERENCES section(id),
|
||||
name VARCHAR(128),
|
||||
display_format VARCHAR(128) NOT NULL -- %d - date, %c - description
|
||||
);",
|
||||
"CREATE TABLE IF NOT EXISTS item (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
row VARCHAR(64) NOT NULL REFERENCES row(id),
|
||||
description VARCHAR(256) NOT NULL,
|
||||
date DATE NOT NULL
|
||||
);",
|
||||
"CREATE TABLE IF NOT EXISTS invites (
|
||||
id VARCHAR(64) PRIMARY KEY
|
||||
);"
|
||||
);
|
||||
|
||||
foreach($queries as $q) {
|
||||
if (!pg_query($db, $q)) {
|
||||
echo "uh oh" . pg_last_error($db);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
echo "ok :verygood:";
|
||||
|
||||
?>
|
100
signup.php
Normal file
100
signup.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
include "config.php";
|
||||
include "helper.php";
|
||||
|
||||
if (is_logged_in()) {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$u = "";
|
||||
$p = "";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$u = strtolower(str_replace(" ", "", $_POST["username"]));
|
||||
$p = $_POST["password"];
|
||||
$v = $_POST["verify"];
|
||||
|
||||
if ($v != $p) {
|
||||
$err = "Passwords don't match";
|
||||
} else if (!($u && $p && $v)) {
|
||||
$err = "Bad request, missing username, password, or verify parameter";
|
||||
} else if (strlen($u) > 64 || strlen($u) === 0) {
|
||||
$err = "Username must be 1-64 chars";
|
||||
} else if (!preg_match("/^[a-z0-9_-]{1,64}$/", $u)) {
|
||||
$err = "Username can only include a-z, 0-9, _, and -";
|
||||
} else {
|
||||
$query = "SELECT count(*) FROM users WHERE username='$u' LIMIT 1;";
|
||||
|
||||
$response = pg_query($db, $query);
|
||||
$c = pg_fetch_array($response)["count"];
|
||||
|
||||
if ($c === "0") {
|
||||
$user_id = generate_id();
|
||||
$pw_hash = password_hash($p, PASSWORD_DEFAULT);
|
||||
$user_parameters = array(
|
||||
"id" => $user_id,
|
||||
"username" => $u,
|
||||
"password_hash" => $pw_hash
|
||||
);
|
||||
|
||||
pg_insert($db, "users", $user_parameters);
|
||||
|
||||
foreach ($default_schema as $section_id => $section_data) {
|
||||
$section_id = generate_id();
|
||||
$section_parameters = array(
|
||||
"id" => $section_id,
|
||||
"users" => $user_id,
|
||||
"name" => $section_data["name"],
|
||||
"show_subtitle" => $section_data["show_subtitle"]
|
||||
);
|
||||
|
||||
pg_insert($db, "section", $section_parameters);
|
||||
|
||||
foreach ($section_data["items"] as $row_id => $row_data) {
|
||||
$row_id = generate_id();
|
||||
$row_parameters = array(
|
||||
"id" => $row_id,
|
||||
"section" => $section_id,
|
||||
"name" => $row_data["name"],
|
||||
"display_format" => $row_data["display_format"]
|
||||
);
|
||||
|
||||
pg_insert($db, "row", $row_parameters);
|
||||
}
|
||||
}
|
||||
|
||||
$token = get_token($u, $pw_hash);
|
||||
setcookie(
|
||||
"token",
|
||||
$token,
|
||||
time() + 60 * 60 * 24 * 30 * 265 // 1 year from now
|
||||
);
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
} else {
|
||||
$err = "User '" . htmlspecialchars($u) . "' already exists";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$title = "Log In";
|
||||
include "boilerplate/head.php";
|
||||
?>
|
||||
|
||||
<form method="POST">
|
||||
<div><input placeholder="Username" name="username" value="<?php echo htmlspecialchars($u); ?>" maxlength="64" required></div>
|
||||
<div><input placeholder="Password" name="password" type="password" value="<?php echo htmlspecialchars($p); ?>" required></div>
|
||||
<div><input placeholder="Verify Password" name="verify" type="password" required></div>
|
||||
<div><input type="submit" value="Log in"></div>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
$q = "SELECT count(*) FROM users LIMIT 1;";
|
||||
|
||||
if ($signups !== false || ($signups === false && pg_fetch_array(pg_query($db, $q))["count"] !== 0)) {
|
||||
echo "<p><a href=\"signup.php\">Sign up instead?</a></p>";
|
||||
}
|
||||
|
||||
include "boilerplate/foot.php";
|
||||
?>
|
Loading…
Reference in a new issue