Compare commits
4 Commits
0b90913b5b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8444e1f23a | ||
|
|
5df09b1f2f | ||
|
|
b568a283ac | ||
|
|
ffc04634ff |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ vendor
|
||||
node_modules
|
||||
database/origo.db
|
||||
scripts/default-content.sh
|
||||
docker-compose.override.yml
|
||||
|
||||
@@ -3,24 +3,14 @@ services:
|
||||
image: dchadwick/planet-express:1.0.1
|
||||
working_dir: /app
|
||||
command: php-fpm -F
|
||||
ports:
|
||||
- "127.0.0.1:9000:9000"
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./database:/app/database
|
||||
networks:
|
||||
- app-network
|
||||
|
||||
web:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "8000:80"
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
depends_on:
|
||||
- php
|
||||
networks:
|
||||
- app-network
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
|
||||
@@ -34,8 +34,7 @@ class ApiWebhookController extends ControllerBase implements ControllerInterface
|
||||
$allowed_origins = [
|
||||
'http://127.0.0.1:8088',
|
||||
'http://localhost:8088',
|
||||
'https://yourproduction-site.com',
|
||||
'https://another-client-site.io'
|
||||
'https://code.danchadwick.dev',
|
||||
];
|
||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||
// Check if the requester is in our whitelist
|
||||
|
||||
@@ -3,20 +3,59 @@
|
||||
namespace Origo\Controller;
|
||||
|
||||
use Origo\Entity\User;
|
||||
use Origo\Controller\ControllerBase;
|
||||
use Origo\Controller\ControllerInterface;
|
||||
use Origo\Services\Template;
|
||||
use Origo\Services\Request;
|
||||
use Origo\Services\Renderer;
|
||||
|
||||
class UserDashboardController implements ControllerInterface {
|
||||
class UserDashboardController extends ControllerBase implements ControllerInterface {
|
||||
|
||||
/**
|
||||
* Get the response for the dashboard.
|
||||
*/
|
||||
public function getResponse(): string {
|
||||
$template = new Template('dashboard.html');
|
||||
$user = new User();
|
||||
$user->loadUserFromSession($this->request->getCookie('ORIGOSESS'));
|
||||
$user_id = $user->get('id');
|
||||
|
||||
return $template->render();
|
||||
$query = $this->database->query("
|
||||
SELECT pv.page, pv.title, pv.referrer, pv.ip, pv.language, pv.screen_res, pv.timestamp
|
||||
FROM pageviews pv
|
||||
JOIN user_accounts ua ON pv.account_id = ua.account_id
|
||||
WHERE ua.user_id = :user_id
|
||||
ORDER BY pv.timestamp DESC
|
||||
");
|
||||
$query->bindParam(':user_id', $user_id);
|
||||
$query->execute();
|
||||
$results = $query->fetchAll();
|
||||
|
||||
$rows = '';
|
||||
if ($results) {
|
||||
foreach ($results as $row) {
|
||||
$page = htmlspecialchars($row['page']);
|
||||
$title = htmlspecialchars($row['title']);
|
||||
$referrer = htmlspecialchars($row['referrer']);
|
||||
$ip = htmlspecialchars($row['ip']);
|
||||
$language = htmlspecialchars($row['language']);
|
||||
$screen_res = htmlspecialchars($row['screen_res']);
|
||||
$timestamp = htmlspecialchars($row['timestamp']);
|
||||
|
||||
$rows .= "
|
||||
<tr>
|
||||
<td>{$title}</td>
|
||||
<td>{$page}</td>
|
||||
<td>{$referrer}</td>
|
||||
<td>{$ip}</td>
|
||||
<td>{$language}</td>
|
||||
<td>{$screen_res}</td>
|
||||
<td>{$timestamp}</td>
|
||||
</tr>";
|
||||
}
|
||||
} else {
|
||||
$rows = '<tr><td colspan="7">No pageviews yet.</td></tr>';
|
||||
}
|
||||
|
||||
$template = new Template('dashboard.html');
|
||||
return $template->render([
|
||||
'pageview_rows' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,30 @@
|
||||
<h4>Actions</h4>
|
||||
<ul id="dash-menu">
|
||||
<li><a href="/planner" class="btn btn-primary"><span class="material-symbols-outlined">edit_calendar</span>Planner</a></li>
|
||||
<li><a href="/analytics" class="btn btn-primary"><span class="material-symbols-outlined">area_chart</span>Analytics</a></li>
|
||||
<li><a href="/user-logout" class="btn btn-accent"><span class="material-symbols-outlined"> logout </span>Logout</a></li>
|
||||
<li><a href="/user-logout" class="btn btn-accent"><span class="material-symbols-outlined">logout</span>Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="pageviews">
|
||||
<h2>Pageviews</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Page</th>
|
||||
<th>Referrer</th>
|
||||
<th>IP</th>
|
||||
<th>Language</th>
|
||||
<th>Screen</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ pageview_rows }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#dashboard {
|
||||
.dashboard-container {
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
padding-bottom: 100px;
|
||||
padding: 0 24px 100px;
|
||||
}
|
||||
|
||||
h1.page-title {
|
||||
@@ -15,11 +15,71 @@
|
||||
justify-content: flex-start;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
#pageviews {
|
||||
margin-top: 2rem;
|
||||
|
||||
h2 {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--color-text-primary);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-light-gray);
|
||||
}
|
||||
|
||||
table.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: var(--font-size-small);
|
||||
white-space: nowrap;
|
||||
|
||||
thead tr {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-soft-cream);
|
||||
|
||||
th {
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
border-bottom: 1px solid var(--color-light-gray);
|
||||
transition: background 0.15s ease;
|
||||
|
||||
&:nth-child(even) {
|
||||
background: var(--color-light-gray);
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background: var(--color-soft-cream);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.65rem 1rem;
|
||||
color: var(--color-text-primary);
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,9 +482,9 @@ img {
|
||||
}
|
||||
|
||||
#dashboard .dashboard-container {
|
||||
max-width: 800px;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
padding-bottom: 100px;
|
||||
padding: 0 24px 100px;
|
||||
}
|
||||
#dashboard h1.page-title {
|
||||
margin-bottom: 0;
|
||||
@@ -501,6 +501,54 @@ img {
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
#dashboard #pageviews {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
#dashboard #pageviews h2 {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--color-text-primary);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
#dashboard #pageviews .table-wrapper {
|
||||
overflow-x: auto;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-light-gray);
|
||||
}
|
||||
#dashboard #pageviews table.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: var(--font-size-small);
|
||||
white-space: nowrap;
|
||||
}
|
||||
#dashboard #pageviews table.data-table thead tr {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-soft-cream);
|
||||
}
|
||||
#dashboard #pageviews table.data-table thead tr th {
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
#dashboard #pageviews table.data-table tbody tr {
|
||||
border-bottom: 1px solid var(--color-light-gray);
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
#dashboard #pageviews table.data-table tbody tr:nth-child(even) {
|
||||
background: var(--color-light-gray);
|
||||
}
|
||||
#dashboard #pageviews table.data-table tbody tr:nth-child(odd) {
|
||||
background: var(--color-soft-cream);
|
||||
}
|
||||
#dashboard #pageviews table.data-table tbody tr:hover {
|
||||
background: var(--color-surface);
|
||||
}
|
||||
#dashboard #pageviews table.data-table tbody tr td {
|
||||
padding: 0.65rem 1rem;
|
||||
color: var(--color-text-primary);
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#task-list .container {
|
||||
max-width: 800px;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["partials/reset.scss","partials/base.scss","partials/header.scss","partials/animations.scss","partials/login-form.scss","partials/403.scss","partials/dashboard.scss","partials/task-list.scss","style.scss"],"names":[],"mappings":"AAAA;AACA;EACE;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;IACE;;;AAIJ;AACE;EACA;AACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;;;AAEF;EACE;;;AAGF;AAAA;AAAA;AAGA;EACE;;;ACpDF;AACE;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;AAEA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;;AC9EN;EACE;EACA;EACA;;;AAIA;EACE;EACA;EACA;;;AAIJ;EACE;;;ACfF;EACE;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;;ACnBF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;AAGF;EACE;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;;;AC9IN;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;IAAW;;EACX;IAAM;;;AAGR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;IAAW;;EACX;IAAM;;EACN;IAAM;;;AAGR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;IAAK;;;AAGP;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;;;AC5MJ;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;ACpBJ;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;ACrDF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA","file":"style.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["partials/reset.scss","partials/base.scss","partials/header.scss","partials/animations.scss","partials/login-form.scss","partials/403.scss","partials/dashboard.scss","partials/task-list.scss","style.scss"],"names":[],"mappings":"AAAA;AACA;EACE;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;IACE;;;AAIJ;AACE;EACA;AACA;EACA;;;AAGF;AACA;EACE;EACA;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;;;AAGF;AACA;EACE;;;AAEF;EACE;;;AAGF;AAAA;AAAA;AAGA;EACE;;;ACpDF;AACE;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;AAEA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;AAEA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;AAEA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;;AC9EN;EACE;EACA;EACA;;;AAIA;EACE;EACA;EACA;;;AAIJ;EACE;;;ACfF;EACE;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;EAEF;IACE;;;ACnBF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;EACA;;AAGF;EACE;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;;;AC9IN;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;IAAW;;EACX;IAAM;;;AAGR;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;IAAW;;EACX;IAAM;;EACN;IAAM;;;AAGR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;IAAK;;;AAGP;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE;IACA;;EAGF;IACE;;;;AC5MJ;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAIJ;EACE;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAKF;EACE;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AC7EV;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;AAGF;AACA;EACE;EACA;EACA;;;ACrDF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA","file":"style.css"}
|
||||
Reference in New Issue
Block a user