Big Update Added Services and Admin
This commit is contained in:
parent
8f462953b7
commit
caca552cae
20
config/routes.php
Normal file
20
config/routes.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
$framework_routes = [
|
||||||
|
'/novaconium' => [
|
||||||
|
'get' => 'NOVACONIUM/init'
|
||||||
|
],
|
||||||
|
'/novaconium/create_admin' => [
|
||||||
|
'post' => 'NOVACONIUM/create_admin'
|
||||||
|
],
|
||||||
|
'/novaconium/login' => [
|
||||||
|
'post' => 'NOVACONIUM/authenticate',
|
||||||
|
'get' => 'NOVACONIUM/login'
|
||||||
|
],
|
||||||
|
'/novaconium/dashboard' => [
|
||||||
|
'get' => 'NOVACONIUM/dashboard'
|
||||||
|
],
|
||||||
|
'/novaconium/logout' => [
|
||||||
|
'post' => 'NOVACONIUM/logout',
|
||||||
|
'get' => 'NOVACONIUM/logout'
|
||||||
|
]
|
||||||
|
];
|
70
controllers/authenticate.php
Normal file
70
controllers/authenticate.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
$v = new Nickyeoman\Validation\Validate();
|
||||||
|
|
||||||
|
$url_success = '/novaconium/dashboard';
|
||||||
|
$url_fail = '/novaconium/login';
|
||||||
|
|
||||||
|
|
||||||
|
// Don't go further if already logged in
|
||||||
|
if ( !empty($session->get('username')) ) {
|
||||||
|
$redirect->url($url_success);
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure Session Token is correct
|
||||||
|
if ($session->get('token') != $post->get('token')) {
|
||||||
|
$messages->addMessage('error', "Invalid Session.");
|
||||||
|
$log->error("Login Authentication - Invalid Session Token");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Username
|
||||||
|
$rawUsername = $post->get('username', null);
|
||||||
|
$cleanUsername = $v->clean($rawUsername); // Clean the input
|
||||||
|
$username = strtolower($cleanUsername); // Convert to lowercase
|
||||||
|
if (!$username) {
|
||||||
|
$messages->addMessage('error', "No Username given.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Password
|
||||||
|
$password = $v->clean($post->get('password', null));
|
||||||
|
if ( empty($password) ) {
|
||||||
|
$messages->addMessage('error', "Password Empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************************************************
|
||||||
|
* Query Database
|
||||||
|
************************************************************************************************************/
|
||||||
|
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
$query = "SELECT id, username, email, password, blocked FROM users WHERE username = ? OR email = ?";
|
||||||
|
$matched = $db->getRow($query, [$username, $username]);
|
||||||
|
if (empty($matched)) {
|
||||||
|
$messages->addMessage('error', "User or Password incorrect.");
|
||||||
|
$log->warning("Login Authentication - Login Error, user doesn't exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
// Re-apply pepper
|
||||||
|
$peppered = hash_hmac('sha3-512', $password, $config['secure_key']);
|
||||||
|
|
||||||
|
// Verify hashed password
|
||||||
|
if (!password_verify($peppered, $matched['password'])) {
|
||||||
|
$messages->addMessage('error', "User or Password incorrect.");
|
||||||
|
$log->warning("Login Authentication - Login Error, password wrong");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Login or Redirect
|
||||||
|
if ($messages->count('error') === 0) {
|
||||||
|
$query = "SELECT groupName FROM user_groups WHERE user_id = ?";
|
||||||
|
$groups = $db->getRow($query, [$matched['id']]);
|
||||||
|
$session->set('username', $cleanUsername);
|
||||||
|
$session->set('group', $groups['groupName']);
|
||||||
|
$redirect->url($url_success);
|
||||||
|
$log->info("Login Authentication - Login Success);
|
||||||
|
} else {
|
||||||
|
$redirect->url($url_fail);
|
||||||
|
}
|
58
controllers/create_admin.php
Normal file
58
controllers/create_admin.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Nickyeoman\Validation;
|
||||||
|
|
||||||
|
$validate = new Validation\Validate();
|
||||||
|
$valid = true;
|
||||||
|
$p = $post->all();
|
||||||
|
|
||||||
|
// Check secure key
|
||||||
|
if (empty($p['secure_key']) || $p['secure_key'] !== $config['secure_key']) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Username
|
||||||
|
$name = $validate->clean($p['username']);
|
||||||
|
if (!$validate->minLength($name, 1)) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email
|
||||||
|
if (empty($p['email'])) {
|
||||||
|
$valid = false;
|
||||||
|
} elseif (!$validate->isEmail($p['email'])) {
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Password
|
||||||
|
if (empty($p['password'])) {
|
||||||
|
$valid = false;
|
||||||
|
} else {
|
||||||
|
// Use pepper + Argon2id
|
||||||
|
$peppered = hash_hmac('sha3-512', $p['password'], $config['secure_key']);
|
||||||
|
$hashed_password = password_hash($peppered, PASSWORD_ARGON2ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
// Insert user
|
||||||
|
$query = <<<EOSQL
|
||||||
|
INSERT INTO `users`
|
||||||
|
(`username`, `password`, `email`, `validate`, `confirmationToken`, `reset`, `created`, `updated`, `confirmed`, `blocked`)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, NULL, NULL, NULL, NOW(), NOW(), 1, 0);
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$params = [$name, $hashed_password, $p['email']];
|
||||||
|
$db->query($query, $params);
|
||||||
|
$userid = $db->lastid();
|
||||||
|
|
||||||
|
// Assign admin group
|
||||||
|
$groupInsertQuery = <<<EOSQL
|
||||||
|
INSERT INTO `user_groups` (`user_id`, `groupName`) VALUES (?, ?);
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($groupInsertQuery, [$userid, 'admin']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always redirect at end
|
||||||
|
$redirect->url('/novaconium');
|
10
controllers/dashboard.php
Normal file
10
controllers/dashboard.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
$data['title'] = 'Novaconium Dashboard Page';
|
||||||
|
|
||||||
|
if ( empty($session->get('username'))) {
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
$messages->error('You are not loggedin');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
view('@novacore/dashboard', $data);
|
128
controllers/init.php
Normal file
128
controllers/init.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'secure_key' => false,
|
||||||
|
'gen_key' => NULL,
|
||||||
|
'users_created' => false,
|
||||||
|
'empty_users' => false,
|
||||||
|
'show_login' => false,
|
||||||
|
'token' => $session->get('token'),
|
||||||
|
'title' => 'Novaconium Admin'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check if SECURE KEY is Set in
|
||||||
|
if ($config['secure_key'] !== null && strlen($config['secure_key']) === 64) {
|
||||||
|
$data['secure_key'] = true;
|
||||||
|
} else {
|
||||||
|
$data['gen_key'] = substr(bin2hex(random_bytes(32)), 0, 64);
|
||||||
|
$log->warn('secure_key not detected');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user table exists
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'users';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(30) NOT NULL,
|
||||||
|
`password` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
`validate` varchar(32) DEFAULT NULL,
|
||||||
|
`confirmationToken` varchar(255) DEFAULT NULL,
|
||||||
|
`reset` varchar(32) DEFAULT NULL,
|
||||||
|
`created` datetime NOT NULL,
|
||||||
|
`updated` datetime DEFAULT NULL,
|
||||||
|
`confirmed` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`blocked` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$data['users_created'] = true;
|
||||||
|
$log->info('Users Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Usergroup
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'user_groups';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `user_groups` (
|
||||||
|
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` INT(11) UNSIGNED NOT NULL,
|
||||||
|
`groupName` VARCHAR(40) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$log->info('User_groups Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Pages Table
|
||||||
|
$query = <<<EOSQL
|
||||||
|
SELECT TABLE_NAME
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = DATABASE()
|
||||||
|
AND TABLE_NAME = 'pages';
|
||||||
|
EOSQL;
|
||||||
|
$result = $db->query($query);
|
||||||
|
|
||||||
|
if ($result->num_rows === 0) {
|
||||||
|
$query = <<<EOSQL
|
||||||
|
CREATE TABLE `pages` (
|
||||||
|
`id` int(11) NOT NULL,
|
||||||
|
`title` varchar(255) NOT NULL,
|
||||||
|
`heading` varchar(255) NOT NULL,
|
||||||
|
`description` varchar(255) NOT NULL,
|
||||||
|
`keywords` varchar(255) NOT NULL,
|
||||||
|
`author` varchar(255) NOT NULL,
|
||||||
|
`slug` varchar(255) NOT NULL,
|
||||||
|
`path` varchar(255) DEFAULT NULL,
|
||||||
|
`intro` text DEFAULT NULL,
|
||||||
|
`body` text DEFAULT NULL,
|
||||||
|
`notes` text DEFAULT NULL,
|
||||||
|
`created` datetime NOT NULL,
|
||||||
|
`updated` datetime DEFAULT NULL,
|
||||||
|
`draft` tinyint(1) NOT NULL DEFAULT 1,
|
||||||
|
`changefreq` varchar(7) NOT NULL DEFAULT 'monthly',
|
||||||
|
`priority` float(4,1) NOT NULL DEFAULT 0.0
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||||
|
EOSQL;
|
||||||
|
|
||||||
|
$db->query($query);
|
||||||
|
$log->info('Pages Table Created');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a user exists
|
||||||
|
$result = $db->query("SELECT COUNT(*) as total FROM users");
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
|
|
||||||
|
if ($row['total'] < 1) {
|
||||||
|
$data['empty_users'] = true;
|
||||||
|
} else {
|
||||||
|
$log->info('Init Run complete, all sql tables exist with a user.');
|
||||||
|
// Everything is working, send them to login page
|
||||||
|
$redirect->url('/novaconium/login');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
|
||||||
|
view('@novacore/init', $data);
|
9
controllers/login.php
Normal file
9
controllers/login.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
$data['title'] = 'Novaconium Login Page';
|
||||||
|
|
||||||
|
// Don't come here if logged in
|
||||||
|
if ($session->get('username')) {
|
||||||
|
$redirect->url('/novaconium/dashboard');
|
||||||
|
makeitso();
|
||||||
|
}
|
||||||
|
view('@novacore/login');
|
5
controllers/logout.php
Normal file
5
controllers/logout.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
$session->kill();
|
||||||
|
$log->info("Logout - Logout Success - " . $_SERVER['REMOTE_ADDR']);
|
||||||
|
$redirect->url('/');
|
||||||
|
makeitso();
|
19
docs/Logs.md
Normal file
19
docs/Logs.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Logging
|
||||||
|
|
||||||
|
You can use the logging class to output to a file.
|
||||||
|
|
||||||
|
|
||||||
|
use ```$log->info(The Message');```1
|
||||||
|
|
||||||
|
Logging levels are:
|
||||||
|
```
|
||||||
|
'DEBUG' => 0,
|
||||||
|
'INFO' => 1,
|
||||||
|
'WARNING' => 2,
|
||||||
|
'ERROR' => 3,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
It's recommended that production is set to ERROR.
|
||||||
|
You set the log level in /App/config.php under 'loglevel' => 'ERROR'
|
||||||
|
If you are using CORXN a health check is run every 30 seconds which would fill the log file with info.
|
3
docs/Messages.md
Normal file
3
docs/Messages.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Messages
|
||||||
|
|
||||||
|
Messages is $messages.
|
5
docs/Post.md
Normal file
5
docs/Post.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Post
|
||||||
|
|
||||||
|
There is a post class.
|
||||||
|
It cleans the post.
|
||||||
|
You can access it with $post.
|
6
docs/Redirect.md
Normal file
6
docs/Redirect.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Redirect
|
||||||
|
|
||||||
|
How to use redirect class.
|
||||||
|
|
||||||
|
$redirect->url;
|
||||||
|
it's called on every page, if you set it more than once the last one is used.
|
5
docs/Session.md
Normal file
5
docs/Session.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Sessions
|
||||||
|
|
||||||
|
There is a sessions handler built into Novaconium.
|
||||||
|
|
||||||
|
$session
|
21
docs/Twig-Views.md
Normal file
21
docs/Twig-Views.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Twig
|
||||||
|
|
||||||
|
## Overrides
|
||||||
|
|
||||||
|
You can override twig templates by creating the same file in the templates directory.
|
||||||
|
|
||||||
|
## Calling View
|
||||||
|
|
||||||
|
There is a $data that the system uses to store arrays for twig you can save to this array:
|
||||||
|
|
||||||
|
```
|
||||||
|
$data['newinfo'] = 'stuff';
|
||||||
|
view('templatename');
|
||||||
|
```
|
||||||
|
and that will automotically go to twig.
|
||||||
|
or you can create a new array and pass it in:
|
||||||
|
|
||||||
|
```
|
||||||
|
$anotherArr['newinfo'] = 'stuff';
|
||||||
|
view('templatename',$anotherArr);
|
||||||
|
```
|
@ -1,3 +0,0 @@
|
|||||||
# Twig Overrides
|
|
||||||
|
|
||||||
You can override twig templates by creating the same file in the templates directory.
|
|
9
services/Auth.php
Normal file
9
services/Auth.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
class Auth
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +1,2 @@
|
|||||||
MYSQL_ROOT_PASSWORD: random
|
MYSQL_ROOT_PASSWORD=random
|
||||||
MYSQL_PASSWORD: random
|
MYSQL_PASSWORD=random
|
||||||
|
3
skeleton/.gitignore
vendored
Normal file
3
skeleton/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
data/
|
||||||
|
novaconium/vendor/
|
||||||
|
novaconium/logs/
|
@ -7,5 +7,8 @@ $config = [
|
|||||||
'pass' => '',
|
'pass' => '',
|
||||||
'port' => 3306
|
'port' => 3306
|
||||||
],
|
],
|
||||||
'base_url' => 'http://localhost:8000'
|
'base_url' => 'http://localhost:8000',
|
||||||
|
'secure_key' => '', //64 alphanumeric characters
|
||||||
|
'logfile' => '/logs/novaconium.log',
|
||||||
|
'loglevel' => 'ERROR' // 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'NONE'
|
||||||
];
|
];
|
||||||
|
@ -30,4 +30,18 @@ code {
|
|||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error, div#debug {
|
||||||
|
border: 1px solid red;
|
||||||
|
padding: 30px;
|
||||||
|
background-color: pink;
|
||||||
|
color: darkred;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#debug {
|
||||||
|
margin-top: 100px;
|
||||||
|
margin-bottom: 100px;
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
class Database {
|
class Database {
|
||||||
|
|
||||||
private $conn;
|
private $conn;
|
||||||
|
public $lastid;
|
||||||
|
|
||||||
public function __construct($dbinfo) {
|
public function __construct($dbinfo) {
|
||||||
$this->conn = new mysqli($dbinfo['host'], $dbinfo['user'], $dbinfo['pass'], $dbinfo['name']);
|
$this->conn = new mysqli($dbinfo['host'], $dbinfo['user'], $dbinfo['pass'], $dbinfo['name']);
|
||||||
@ -13,43 +14,90 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function query($query, $params = []) {
|
public function query($query, $params = []) {
|
||||||
// Prepare the SQL query
|
// Clean up pending results to avoid "commands out of sync"
|
||||||
if ($stmt = $this->conn->prepare($query)) {
|
while ($this->conn->more_results() && $this->conn->next_result()) {
|
||||||
// Bind parameters to the prepared statement (if any)
|
if ($res = $this->conn->use_result()) {
|
||||||
if (!empty($params)) {
|
$res->free();
|
||||||
$types = str_repeat('s', count($params)); // Assuming all params are strings
|
|
||||||
$stmt->bind_param($types, ...$params);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Execute the statement
|
|
||||||
if (!$stmt->execute()) {
|
// Prepare the SQL statement
|
||||||
throw new Exception("Query execution failed: " . $stmt->error);
|
$stmt = $this->conn->prepare($query);
|
||||||
}
|
if (!$stmt) {
|
||||||
|
|
||||||
// Return the statement result
|
|
||||||
return $stmt;
|
|
||||||
} else {
|
|
||||||
throw new Exception("Query preparation failed: " . $this->conn->error);
|
throw new Exception("Query preparation failed: " . $this->conn->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind parameters if needed
|
||||||
|
if (!empty($params)) {
|
||||||
|
$types = str_repeat('s', count($params)); // Use 's' for all types, or detect types dynamically
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the statement
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
$stmt->close();
|
||||||
|
throw new Exception("Query execution failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save last insert id if it's an INSERT query
|
||||||
|
if (preg_match('/^\s*INSERT/i', $query)) {
|
||||||
|
$this->lastid = $this->conn->insert_id;
|
||||||
|
} else {
|
||||||
|
$this->lastid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide what to return
|
||||||
|
// For SELECT/SHOW etc., return result set
|
||||||
|
if (preg_match('/^\s*(SELECT|SHOW|DESCRIBE|EXPLAIN)/i', $query)) {
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
$stmt->close();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For INSERT/UPDATE/DELETE, return success status
|
||||||
|
$success = $stmt->affected_rows;
|
||||||
|
$stmt->close();
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lastid() {
|
||||||
|
return $this->lastid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRow($query, $params = []) {
|
public function getRow($query, $params = []) {
|
||||||
try {
|
try {
|
||||||
// Perform the query using prepared statement
|
// Prepare the SQL statement
|
||||||
$stmt = $this->query($query, $params);
|
$stmt = $this->conn->prepare($query);
|
||||||
|
if (!$stmt) {
|
||||||
// Get the result of the query
|
throw new Exception("Query preparation failed: " . $this->conn->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind parameters
|
||||||
|
if (!empty($params)) {
|
||||||
|
$types = str_repeat('s', count($params)); // You may improve this with actual type detection
|
||||||
|
$stmt->bind_param($types, ...$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the statement
|
||||||
|
if (!$stmt->execute()) {
|
||||||
|
$stmt->close();
|
||||||
|
throw new Exception("Query execution failed: " . $stmt->error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get result
|
||||||
$result = $stmt->get_result();
|
$result = $stmt->get_result();
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
// Fetch the first row from the result
|
|
||||||
return $result->fetch_assoc();
|
$stmt->close();
|
||||||
|
return $row;
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// Handle the exception (log it, display a message, etc.)
|
|
||||||
echo "An error occurred: " . $e->getMessage();
|
echo "An error occurred: " . $e->getMessage();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getRows($query, $params = []) {
|
public function getRows($query, $params = []) {
|
||||||
$stmt = $this->conn->prepare($query);
|
$stmt = $this->conn->prepare($query);
|
||||||
if (!$stmt) {
|
if (!$stmt) {
|
||||||
|
48
src/Logger.php
Normal file
48
src/Logger.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
class Logger {
|
||||||
|
protected string $logFile;
|
||||||
|
protected int $logLevelThreshold;
|
||||||
|
|
||||||
|
const LEVELS = [
|
||||||
|
'DEBUG' => 0,
|
||||||
|
'INFO' => 1,
|
||||||
|
'WARNING' => 2,
|
||||||
|
'ERROR' => 3,
|
||||||
|
'NONE' => 999
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(string $logFile, string $minLevel = 'DEBUG') {
|
||||||
|
$this->logFile = $logFile;
|
||||||
|
$minLevel = strtoupper($minLevel);
|
||||||
|
$this->logLevelThreshold = self::LEVELS[$minLevel] ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function log(string $level, string $message): void {
|
||||||
|
$level = strtoupper($level);
|
||||||
|
if (!isset(self::LEVELS[$level]) || self::LEVELS[$level] < $this->logLevelThreshold) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = date('Y-m-d H:i:s');
|
||||||
|
$ip = $_SERVER['REMOTE_ADDR'];
|
||||||
|
$logEntry = "[$time] [$ip] [$level] $message" . PHP_EOL;
|
||||||
|
|
||||||
|
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function debug(string $msg): void {
|
||||||
|
$this->log('DEBUG', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function info(string $msg): void {
|
||||||
|
$this->log('INFO', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function warning(string $msg): void {
|
||||||
|
$this->log('WARNING', $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function error(string $msg): void {
|
||||||
|
$this->log('ERROR', $msg);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,16 @@ class MessageHandler {
|
|||||||
'success' => []
|
'success' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function __construct(array $sessionMessages = [])
|
||||||
|
{
|
||||||
|
// Merge existing session messages into the default structure
|
||||||
|
foreach ($this->messages as $type => $_) {
|
||||||
|
if (isset($sessionMessages[$type]) && is_array($sessionMessages[$type])) {
|
||||||
|
$this->messages[$type] = $sessionMessages[$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a message of a specific type
|
// Add a message of a specific type
|
||||||
public function addMessage($type, $message) {
|
public function addMessage($type, $message) {
|
||||||
if (!isset($this->messages[$type])) {
|
if (!isset($this->messages[$type])) {
|
||||||
@ -16,11 +26,22 @@ class MessageHandler {
|
|||||||
$this->messages[$type][] = $message;
|
$this->messages[$type][] = $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function error($message){
|
||||||
|
$this->addMessage('error', $message);
|
||||||
|
}
|
||||||
|
|
||||||
// Get all messages of a specific type
|
// Get all messages of a specific type
|
||||||
public function getMessages($type) {
|
public function getMessages($type) {
|
||||||
return $this->messages[$type] ?? [];
|
return $this->messages[$type] ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all messages of a specific type
|
||||||
|
public function showMessages($type) {
|
||||||
|
$result = $this->messages[$type] ?? [];
|
||||||
|
$this->messages[$type] = []; // Clear messages after showing
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
// Get all messages of all types
|
// Get all messages of all types
|
||||||
public function getAllMessages() {
|
public function getAllMessages() {
|
||||||
return $this->messages;
|
return $this->messages;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Use
|
* Use
|
||||||
* $redirect->to('/login');
|
* $redirect->url('/login');
|
||||||
* to trigger a redirect
|
* to trigger a redirect
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -19,12 +19,13 @@ class Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function loadRoutes() {
|
private function loadRoutes() {
|
||||||
|
require_once(FRAMEWORKPATH . '/config/routes.php');
|
||||||
// Check if Path exists
|
// Check if Path exists
|
||||||
if (file_exists(BASEPATH . '/App/routes.php')) {
|
if (file_exists(BASEPATH . '/App/routes.php')) {
|
||||||
require_once( BASEPATH . '/App/routes.php');
|
require_once( BASEPATH . '/App/routes.php');
|
||||||
} else {
|
|
||||||
require_once(FRAMEWORKPATH . '/defaults/App/routes.php');
|
|
||||||
}
|
}
|
||||||
|
$routes = array_merge((array)$routes, (array)$framework_routes);
|
||||||
|
|
||||||
return $routes;
|
return $routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,9 @@ class Router {
|
|||||||
|
|
||||||
// one to one match
|
// one to one match
|
||||||
if (array_key_exists($this->path, $this->routes)) {
|
if (array_key_exists($this->path, $this->routes)) {
|
||||||
return $this->routes[$this->path][$this->requestType];
|
if (!empty($this->routes[$this->path][$this->requestType])) {
|
||||||
|
return $this->routes[$this->path][$this->requestType];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->routes as $key => $value) {
|
foreach ($this->routes as $key => $value) {
|
||||||
@ -103,8 +106,13 @@ class Router {
|
|||||||
|
|
||||||
// checks if the file exists, sets file path
|
// checks if the file exists, sets file path
|
||||||
private function setRouteFile() {
|
private function setRouteFile() {
|
||||||
|
|
||||||
$cp = BASEPATH . '/App/controllers/' . $this->controller . '.php';
|
if (str_starts_with($this->controller, 'NOVACONIUM')) {
|
||||||
|
$trimmed = substr($this->controller, strlen('NOVACONIUM/'));
|
||||||
|
$cp = FRAMEWORKPATH . '/controllers/' . $trimmed . '.php';
|
||||||
|
} else {
|
||||||
|
$cp = BASEPATH . '/App/controllers/' . $this->controller . '.php';
|
||||||
|
}
|
||||||
|
|
||||||
if (file_exists($cp)) {
|
if (file_exists($cp)) {
|
||||||
return $cp;
|
return $cp;
|
||||||
|
@ -5,8 +5,7 @@ class Session {
|
|||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
session_start();
|
session_start();
|
||||||
if (!isset($_SESSION)) {
|
if (!isset($_SESSION['token'])) {
|
||||||
$this->session = $_SESSION;
|
|
||||||
$this->setToken();
|
$this->setToken();
|
||||||
$this->session['messages'] = [];
|
$this->session['messages'] = [];
|
||||||
} else {
|
} else {
|
||||||
@ -51,6 +50,7 @@ class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function kill() {
|
public function kill() {
|
||||||
|
$_SESSION = [];
|
||||||
session_destroy();
|
session_destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ function dd(...$vars) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeitso() {
|
function makeitso() {
|
||||||
global $session, $db, $redirect, $config, $messages;
|
global $session, $db, $redirect, $config, $messages, $log;
|
||||||
|
|
||||||
if (!empty($config['database']['host'])) {
|
if (!empty($config['database']['host'])) {
|
||||||
$db->close();
|
$db->close();
|
||||||
|
@ -10,19 +10,32 @@ if (file_exists(BASEPATH . '/App/config.php')) {
|
|||||||
require_once(FRAMEWORKPATH . '/defaults/App/config.php');
|
require_once(FRAMEWORKPATH . '/defaults/App/config.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
require_once(FRAMEWORKPATH . '/src/Logger.php');
|
||||||
|
$log = new Logger(BASEPATH . $config['logfile'], $config['loglevel']);
|
||||||
|
|
||||||
// Global Functions
|
// Global Functions
|
||||||
require_once(FRAMEWORKPATH . '/src/functions.php');
|
require_once(FRAMEWORKPATH . '/src/functions.php');
|
||||||
|
|
||||||
// Creates the view() function using twig
|
// Creates the view() function using twig
|
||||||
|
$data = array();
|
||||||
require_once(FRAMEWORKPATH . '/src/twig.php');
|
require_once(FRAMEWORKPATH . '/src/twig.php');
|
||||||
|
|
||||||
// Messages
|
|
||||||
require_once(FRAMEWORKPATH . '/src/MessageHandler.php');
|
|
||||||
$messages = new MessageHandler;
|
|
||||||
|
|
||||||
// Start a Session
|
// Start a Session
|
||||||
require_once(FRAMEWORKPATH . '/src/Session.php');
|
require_once(FRAMEWORKPATH . '/src/Session.php');
|
||||||
$session = new Session();
|
$session = new Session();
|
||||||
|
$data['token'] = $session->get('token');
|
||||||
|
if ($config['loglevel'] == 'DEBUG') {
|
||||||
|
$data['debug'] = nl2br(print_r($session->debug(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
require_once(FRAMEWORKPATH . '/src/MessageHandler.php');
|
||||||
|
$messages = new MessageHandler($session->flash('messages'));
|
||||||
|
|
||||||
|
foreach (['error','notice'] as $key){
|
||||||
|
$data[$key] = $messages->showMessages($key);
|
||||||
|
}
|
||||||
|
|
||||||
// Load Database Class
|
// Load Database Class
|
||||||
if (!empty($config['database']['host'])) {
|
if (!empty($config['database']['host'])) {
|
||||||
|
16
src/twig.php
16
src/twig.php
@ -1,11 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
//Twig
|
//Twig
|
||||||
function view($name = '', $data = []) {
|
function view($name = '', $moreData = []) {
|
||||||
global $config; // Use the globally included $config
|
global $config, $data; // Use the globally included $config
|
||||||
|
|
||||||
|
if (!empty($moreData)){
|
||||||
|
$data = array_merge($data, $moreData);
|
||||||
|
}
|
||||||
|
|
||||||
$loader = new Twig\Loader\FilesystemLoader(BASEPATH . '/App/views/');
|
$loader = new Twig\Loader\FilesystemLoader(BASEPATH . '/App/views/');
|
||||||
$loader->addPath(BASEPATH . '/vendor/4lt/novaconium/twig', 'novaconium');
|
$loader->addPath(FRAMEWORKPATH . '/twig', 'novaconium');
|
||||||
|
$loader->addPath(FRAMEWORKPATH . '/views', 'novacore');
|
||||||
$loader->addPath(BASEPATH . '/App/templates', 'override');
|
$loader->addPath(BASEPATH . '/App/templates', 'override');
|
||||||
|
|
||||||
$twig = new Twig\Environment($loader);
|
$twig = new Twig\Environment($loader);
|
||||||
@ -15,7 +20,10 @@ function view($name = '', $data = []) {
|
|||||||
|
|
||||||
// Check if the template exists
|
// Check if the template exists
|
||||||
if (file_exists(BASEPATH . '/App/views/' . $name . '.html.twig')) {
|
if (file_exists(BASEPATH . '/App/views/' . $name . '.html.twig')) {
|
||||||
echo $twig->render("$name.html.twig", $data);
|
echo $twig->render($name . '.html.twig', $data);
|
||||||
|
return true;
|
||||||
|
} elseif (str_starts_with($name, '@')) { // Check if using framework
|
||||||
|
echo $twig->render($name . '.html.twig', $data);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
echo "Error: Twig Template ($name) Not Found.";
|
echo "Error: Twig Template ($name) Not Found.";
|
||||||
|
@ -47,6 +47,13 @@
|
|||||||
{% include ['@override/footer.html.twig', '@novaconium/footer.html.twig'] %}
|
{% include ['@override/footer.html.twig', '@novaconium/footer.html.twig'] %}
|
||||||
{% block footerafter %}{% endblock %}
|
{% block footerafter %}{% endblock %}
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
{% if debug is not empty %}
|
||||||
|
<div id="debug">
|
||||||
|
<h2>Debugging Information</h2>
|
||||||
|
{{ debug|raw }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% include ['@override/foot.html.twig', '@novaconium/foot.html.twig'] %}
|
{% include ['@override/foot.html.twig', '@novaconium/foot.html.twig'] %}
|
||||||
</body></html>
|
</body></html>
|
||||||
|
9
views/dashboard.html.twig
Normal file
9
views/dashboard.html.twig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<p>Dashboard page</p>
|
||||||
|
<p><a href="/">Homepage</a></p>
|
||||||
|
<p><a href="/novaconium/logout">logout</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
67
views/init.html.twig
Normal file
67
views/init.html.twig
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
|
||||||
|
{% if not secure_key %}
|
||||||
|
<div id="secure_key">
|
||||||
|
<h2>Secure Key</h2>
|
||||||
|
<p>Please set the <code>secure_key</code> in <code>App/config.php</code> to an alphanumeric code that is 64 characters in length.</p>
|
||||||
|
<p>You can generate a secure key like this:</p>
|
||||||
|
<pre><code>pwgen -sB 64 1</code></pre>
|
||||||
|
<p>Or use this one:</p>
|
||||||
|
<pre><code>{{gen_key}}</code></pre>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if users_created %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Users Table Created</h2>
|
||||||
|
<p>There was no users table in the database. One was created.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if empty_users and secure_key %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Create Admin</h2>
|
||||||
|
<p>No admin users exist, make an admin user now.</p>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/create_admin">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="email">Email:</label><br>
|
||||||
|
<input type="text" id="email" name="email" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<label for="secure_key">Secure Key <i>The <code>secure_key</code> from your config.php file</i>:</label><br>
|
||||||
|
<input type="text" id="secure_key" name="secure_key" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Create User</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if show_login %}
|
||||||
|
<div id="users_created">
|
||||||
|
<h2>Administrator Login</h2>
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/login">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
23
views/login.html.twig
Normal file
23
views/login.html.twig
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{% extends '@novaconium/master.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="login_form">
|
||||||
|
|
||||||
|
<form method="post" action="/novaconium/login">
|
||||||
|
<input type="hidden" name="token" value="{{ token }}" />
|
||||||
|
|
||||||
|
<label for="username">Username:</label><br>
|
||||||
|
<input type="text" id="username" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label for="password">Password:</label><br>
|
||||||
|
<input type="password" id="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user