add this to git finally

This commit is contained in:
2025-05-19 12:51:46 +03:00
parent 7a8e69d87d
commit 11b7696c19
13 changed files with 760 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
db
.php-*
REPORT_CONTENTS

214
database.php Normal file
View File

@ -0,0 +1,214 @@
<?php
include_once 'utils.php';
$RESULT_MODE = SQLITE3_BOTH;
session_start();
function connectToRedis()
{
$redis_host = '127.0.0.1';
$redis_port = 6379;
try {
$redis = new Redis();
if ($redis->connect($redis_host, $redis_port)) {
/* if ($redis_password) { */
/* $redis->auth($redis_password); */
/* } */
return $redis;
}
return false;
} catch (Exception $e) {
error_log("Redis connection error: ".$e->getMessage());
return false;
}
}
function initializeNewUser()
{
$redis = connectToRedis();
if (!$redis) {
return false;
}
$user_id = generateUUID();
$redis->hSet('users', $user_id, json_encode([
'created_at' => time(),
'status' => 'active'
]));
$db_file = "db/$user_id.db";
$db = new SQLite3($db_file);
$schema_sql = file_get_contents('db/schema.sql');
if (!$schema_sql) {
error_log("Failed to read schema file: $schema_path");
return false;
}
$db->exec($schema_sql);
if (!$db) {
// Cleanup Redis entry if db creation fails
$redis->hDel('users', $user_id);
return false;
}
$db->close();
return ["user_id" => $user_id, "db_file" => "db/$user_id.db"];
}
function initializeApp($existing_user_id = null)
{
if ($existing_user_id) {
/* $_SESSION["user_id"] = $existing_user_id; */
} else {
return initializeNewUser();
}
}
function getDB($user_id = null)
{
if (!is_null($user_id)) {
$db_file = "db/$user_id.db";
if (!file_exists($db_file)) {
return createUserDatabase($user_id);
}
return new SQLite3($db_file);
}
}
function registerUser($username, $email, $password)
{
$db = getDB($_COOKIE['user_id']);
$query = "INSERT INTO users(username, email, password) values ('$username', '$email', '$password');";
return $db->exec($query);
}
function loginUser($email, $password)
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT username, password FROM users WHERE email = '$email';";
$result = $db->query($query)->fetchArray($RESULT_MODE);
if ($result["password"] === $password) {
return $result['username'];
} else {
return null;
}
}
function getUserById($id)
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT username, email FROM users WHERE id = $id";
try {
$result = $db->query($query);
return $result->fetchArray($RESULT_MODE);
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function createProduct($title, $amountInStock)
{
$db = getDB($_COOKIE['user_id']);
$query = "INSERT INTO products(title, amount_in_stock) values ('$title', $amountInStock)";
try {
return $db->exec($query);
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function getProductsByTitle($title)
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT * FROM products WHERE title like '".$title."%'";
error_log($query, 0);
try {
$result = $db->query($query);
if ($result === false) {
error_log("{$db->lastErrorCode()}", 0);
throw new Exception($db->lastErrorMsg());
}
$products = [];
while ($row = $result->fetchArray($RESULT_MODE)) {
$products[] = $row;
}
return $products;
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function getAllProducts()
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT * FROM products";
try {
$result = $db->query($query);
$products = [];
while ($row = $result->fetchArray($RESULT_MODE)) {
$products[] = $row;
}
return $products;
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function deleteProductByTitle($title)
{
$db = getDB($_COOKIE['user_id']);
$query = "DELETE FROM products where title='$title'";
try {
return $db->exec($query);
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function getOrdersForUser($userId)
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT * FROM orders WHERE user_id = $userId";
try {
$result = $db->query($query);
return $result->fetchArray($RESULT_MODE);
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}
function getOrdersForProduct($productId)
{
global $RESULT_MODE;
$db = getDB($_COOKIE['user_id']);
$query = "SELECT * FROM orders WHERE product_id = $productId";
try {
$result = $db->query($query);
return $result->fetchArray($RESULT_MODE);
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage()."\n";
}
}

40
header.php Normal file
View File

@ -0,0 +1,40 @@
<header>
<nav class="nav-links">
<a href="/users">Users</a>
<a href="/products">Products</a>
<a href="/orders">Orders</a>
</nav>
<div class="auth-section">
<?php
session_start(); if(isset($_SESSION['username']) && !empty($_SESSION['username'])): ?>
<div class="user-info">
<span class="username">Welcome, <?=htmlspecialchars($_SESSION['username'])?></span>
<a href="/logout" class="logout-link">Log out</a>
</div>
<?php else: ?>
<?php if (isset($_SESSION['error_message'])): ?>
<p style="color: red; text-align: center;"><?= $_SESSION['error_message'] ?></p>
<?php endif; ?>
<form id="authenticate-form" action="/login" method="post">
<input type="text" name="username" placeholder="Username (*for registration*)">
<input type="text" name="email" placeholder="Email" required>
<input type="password" name="password" placeholder="Password" required>
<button type="button" onclick="submitForm('login');">Login</button>
<button type="button" class="register-btn" onclick="submitForm('register');">Register</button>
</form>
<script>
function submitForm(action) {
const form = document.getElementById('authenticate-form');
form.action = `/${action}`;
if (form.checkValidity()) {
form.submit();
} else {
form.reportValidity();
}
}
</script>
<?php endif; ?>
</div>
</header>

122
index.css Normal file
View File

@ -0,0 +1,122 @@
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 10px;
}
.product {
background: #f5f5f5;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
label {
display: block;
margin-bottom: 5px;
}
input, textarea, select {
width: 100%;
padding: 8px;
margin-bottom: 10px;
max-width: 400px;
}
button {
padding: 0.5rem 1rem;
background: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
border-radius: 3px;
text-decoration: none;
display: inline-block;
}
button:hover {
background-color: #45a049;
}
.delete {
background: #f44336;
}
.search {
background: #2196F3;
}
.warning {
color: #f44336;
font-weight: bold;
}
header {
background-color: #333;
color: white;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-links {
display: flex;
gap: 1.5rem;
}
.nav-links a {
color: white;
text-decoration: none;
font-weight: bold;
}
.nav-links a:hover {
text-decoration: underline;
}
.auth-section {
display: flex;
align-items: start;
gap: 0.75rem;
}
#authenticate-form {
display: flex;
gap: 0.5rem;
align-items: start;
}
#authenticate-form input {
padding: 0.5rem;
border: none;
border-radius: 3px;
}
.register-btn {
background-color: #2196F3;
}
.register-btn:hover {
background-color: #0b7dda;
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
.username {
font-weight: bold;
}
.logout-link {
color: #ff9999;
text-decoration: none;
}
.logout-link:hover {
text-decoration: underline;
}
#product-forms {
padding: 0px 10px;
display: flex;
justify-content: space-between;
}

49
index.php Normal file
View File

@ -0,0 +1,49 @@
<?php
include_once 'database.php';
$request = $_SERVER['REQUEST_URI'];
$path = parse_url($request, PHP_URL_PATH);
if (isset($_COOKIE["user_id"])) {
error_log('cookie is set', 0);
} else {
error_log('cookie is NOT set', 0);
$ids = initializeApp(null);
setcookie("user_id", $ids["user_id"], time() + 3600, "/");
setcookie("db_file", $ids["db_file"], time() + 3600, "/");
$_COOKIE["user_id"] = $ids["user_id"];
$_COOKIE["db_file"] = $ids["db_file"];
}
/* TODO: remove for prod code */
$file_path = __DIR__.$request;
$extension = pathinfo($file_path, PATHINFO_EXTENSION);
if ($extension === 'css') {
header('Content-Type: text/css');
readfile($file_path);
exit;
}
/* TODO: remove for prod code */
switch($path) {
case '': case '/': case '/products':
require __DIR__.'/products.php';
break;
case '/login':
require __DIR__.'/login.php';
break;
case '/register':
require __DIR__.'/register.php';
break;
case '/logout':
require __DIR__.'/logout.php';
break;
case '/orders':
require __DIR__.'/orders.php';
break;
case '/users':
require __DIR__.'/users.php';
break;
}
?>

20
login.php Normal file
View File

@ -0,0 +1,20 @@
<?php
include_once 'database.php';
session_start();
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$email = $_POST["email"];
$password = $_POST["password"];
$username = loginUser($email, $password);
if (!is_null($username)) {
$_SESSION['error_message'] = null;
$_SESSION['username'] = $username;
$_SESSION['email'] = $email;
} else {
$_SESSION['error_message'] = "Invalid email or password";
}
header("Location: /products");
}
?>

10
logout.php Normal file
View File

@ -0,0 +1,10 @@
<?php
session_start();
/* $personalDb = "db/{$_SESSION["user_id"]}.db"; */ // TODO: put it in cookie
/* if (file_exists($personalDb)) { */
/* unlink($personalDb); */
/* } */
session_unset();
session_destroy();
header("Location: /products");
?>

13
orders.php Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Orders - WIP</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<?php include 'header.php'; ?>
<h1>Orders - Work in progress!</h1>
</body>
</html>

73
products.php Normal file
View File

@ -0,0 +1,73 @@
<?php
include_once 'database.php';
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'POST') {
if (isset($_POST['create'])) {
$title = $_POST['title'];
$amountInStock = $_POST['in_stock'];
createProduct($title, $amountInStock);
}
} elseif ($method === 'GET') {
if (isset($_GET["title"])) {
$title = $_GET["title"];
}
if (!is_null($title)) {
$products = getProductsByTitle($title);
}
}
if (!isset($products)) {
$products = getAllProducts();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Products page</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<?php include 'header.php'; ?>
<div id="product-forms">
<div id="add-product">
<h2>Add new Product</h2>
<form method="POST">
<label for "title">Title:</label>
<input type="text" name="title" required>
<label for "in_stock">Amount in stock:</label>
<input type="text" name="in_stock" required>
<br>
<button type="submit" name="create">Create product</button>
</form>
</div>
<div id="search-product-by-title">
<h2>Find product by title</h2>
<form method="GET" action="/products">
<label for "title">Title:</label>
<input type="text" name="title" required>
<br>
<button type="submit">Search</button>
</form>
</div>
</div>
<h2>Products:</h2>
<?php if (empty($products)): ?>
<p>No products found.</p>
<?php else: ?>
<?php foreach ($products as $prod): ?>
<div class="product">
<h3><?= $prod[1] ?></h3>
<p>In stock: <?= $prod[2] ?> items</p>
<p><strong>ID: <?= $prod[0] ?></strong></p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</body>
</html>

21
register.php Normal file
View File

@ -0,0 +1,21 @@
<?php
include_once 'database.php';
session_start();
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$result = registerUser($username, $email, $password);
if ($result) {
$_SESSION['error_message'] = null;
$_SESSION['username'] = $username;
$_SESSION['email'] = $email;
} else {
$_SESSION['error_message'] = "User with this username already exists!";
}
header("Location: /products");
}
?>

167
tutorial.php Normal file
View File

@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vulnerability testing</title>
</head>
<body>
<h1>Welcome to vulnerability testing! Powered by PHP!</h1>
<?= 'Hello world!' ?>
<?php
// datatypes are inferred at runtime
$name = "John";
$age = 30;
$height = 1.85;
$isActive = true;
$skills = ["PHP", "JS"];
$person = null;
echo nl2br("\n\n");
var_dump($skills);
?>
<?php
// type juggling
$a = "5";
$b = 2;
$c = $a+$b;
echo nl2br("\n\ntype juggling:\n");
var_dump($c); // whaddaya think? of course, it's int(7)
?>
<?php
if ($age > 18) {
echo "Adult";
} elseif ($age > 12) {
echo "Teenager";
} else {
echo "Child";
}
echo nl2br("\n\n");
switch($day) {
case "Monday":
echo "Start of week";
break;
case "Friday":
echo "End of week";
break;
default:
echo "Mid-week";
}
echo nl2br("\n\n");
for ($i = 0; $i < 5; $i++) {
echo $i;
}
echo nl2br("\n\n");
$fruits = ["apple", "banana", "orange"];
foreach($fruits as $fruit) {
echo $fruit;
}
echo nl2br("\n\n");
foreach($fruits as $index => $fruit) {
echo "$index: $fruit";
}
echo nl2br("\n\n");
$i = 0;
while ($i < 5) {
echo $i++;
}
echo nl2br("\n\n");
?>
<?php
$name = "John";
$age = 20;
echo "Hello, $name! You are $age years old!";
// for complex, use squirly braces {}
/* echo "Hello, {$name}! In 5 years, you'll be {$age + 5} years old."; */ // HACK: doesn't work for some way?
// NOTE: single quote strings don't interpolate!!!
echo nl2br("\n\n");
?>
<?php
function greet($name, $greeting = "Hello") {
return "$greeting, $name!";
}
echo greet("John");
echo greet("Mary", "Hi");
$multiply = function($a, $b) {
return $a * $b;
};
echo $multiply(3,4);
echo nl2br("\n\n");
?>
<?php
class Person {
public $name;
private $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function greet() {
return "Hello, my name is {$this->name} and I'm {$this->age} years old.";
}
public function getAge() {
return $this->age;
}
}
$person = new Person("John", 30);
echo $person->greet();
echo $person->getAge();
echo nl2br("\n\n");
?>
<?php
$fruits = ["apple", "banana", "orange"];
echo $fruits[0];
echo nl2br("\n");
$person = [
"name" => "John",
"age" => 30,
"city" => "NewYork"
];
echo $person["name"];
echo nl2br("\n");
$users = [
["name" => "John", "age" => 30],
["name" => "Mary", "age" => 25],
];
echo $users[1]["name"];
echo nl2br("\n");
$fruits[] = "grape"; // add item to the end
$person["job"] = "developer"; // add new K-V pair
$count = count($fruits);
$exists = in_array("apple", $fruits);
sort($fruits);
$keys = array_keys($person);
$values = array_values($person);
echo nl2br("\n\n");
?>
</body>
</html>

13
users.php Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Users - WIP</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<?php include 'header.php'; ?>
<h1>Users - Work in progress!</h1>
</body>
</html>

15
utils.php Normal file
View File

@ -0,0 +1,15 @@
<?php
function generateUUID()
{
// Generate 16 random bytes
$data = random_bytes(16);
// Set version to 0100 (UUID v4)
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// Set bits 6-7 to 10
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
// Format the UUID
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
?>