Skip to content

Commit

Permalink
PUSH
Browse files Browse the repository at this point in the history
-> Support pin
-> Components for homepage
  • Loading branch information
NaysKutzu committed Jan 14, 2025
1 parent 57491eb commit fdc99e7
Show file tree
Hide file tree
Showing 17 changed files with 601 additions and 436 deletions.
16 changes: 16 additions & 0 deletions backend/app/Api/User/Session/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,21 @@
}
});

$router->add('/api/user/session/newPin', function (): void {
App::init();
$appInstance = App::getInstance(true);
$appInstance->allowOnlyPOST();
$session = new Session($appInstance);
$pin = $appInstance->generatePin();
try {
$session->setInfo(UserColumns::SUPPORT_PIN, $pin, false);
$appInstance->OK('Support pin updated successfully!', ['pin' => $pin]);
} catch (Exception $e) {
$appInstance->getLogger()->error('Failed to generate new pin: ' . $e->getMessage());
$appInstance->BadRequest('Bad Request', ['error_code' => 'DB_ERROR', 'error' => $e->getMessage()]);
}
});

$router->get('/api/user/session', function (): void {
App::init();
$appInstance = App::getInstance(true);
Expand All @@ -226,6 +241,7 @@
UserColumns::USERNAME,
UserColumns::EMAIL,
UserColumns::VERIFIED,
UserColumns::SUPPORT_PIN,
UserColumns::BANNED,
UserColumns::TWO_FA_BLOCKED,
UserColumns::TWO_FA_ENABLED,
Expand Down
11 changes: 11 additions & 0 deletions backend/app/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ public function decrypt(string $data): string
return XChaCha20::decrypt($data, $_ENV['DATABASE_ENCRYPTION_KEY'], true);
}

/**
* Generate a random code.
*/
public function generateCode(): string
{
$code = base64_encode(random_bytes(64));
Expand All @@ -366,4 +369,12 @@ public function generateCode(): string

return $code;
}

/**
* Generate a random pin.
*/
public function generatePin(): int
{
return random_int(100000, 999999);
}
}
5 changes: 3 additions & 2 deletions backend/app/Chat/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ public static function register(string $username, string $password, string $emai
*/
$stmt = $pdoConnection->prepare('
INSERT INTO ' . self::TABLE_NAME . '
(username, first_name, last_name, email, password, avatar, background, uuid, token, role, first_ip, last_ip, banned, verified)
(username, first_name, last_name, email, password, avatar, background, uuid, token, role, first_ip, last_ip, banned, verified, support_pin)
VALUES
(:username, :first_name, :last_name, :email, :password, :avatar, :background, :uuid, :token, :role, :first_ip, :last_ip, :banned, :verified)
(:username, :first_name, :last_name, :email, :password, :avatar, :background, :uuid, :token, :role, :first_ip, :last_ip, :banned, :verified, :support_pin)
');
$password = App::getInstance(true)->encrypt($password);

Expand All @@ -186,6 +186,7 @@ public static function register(string $username, string $password, string $emai
':last_ip' => $ip,
':banned' => 'NO',
':verified' => 'false',
':support_pin' => App::getInstance(true)->generatePin(),
]);
\MythicalClient\MythicalSystems\Telemetry::send(\MythicalClient\MythicalSystems\TelemetryCollection::USER_NEW);
/**
Expand Down
2 changes: 2 additions & 0 deletions backend/app/Chat/columns/UserColumns.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class UserColumns
public const LAST_IP = 'last_ip';
public const BANNED = 'banned';
public const VERIFIED = 'verified';
public const SUPPORT_PIN = 'support_pin';
public const TWO_FA_ENABLED = '2fa_enabled';
public const TWO_FA_KEY = '2fa_key';
public const TWO_FA_BLOCKED = '2fa_blocked';
Expand All @@ -143,6 +144,7 @@ public static function getColumns(): array
self::FIRST_IP,
self::LAST_IP,
self::BANNED,
self::SUPPORT_PIN,
self::VERIFIED,
self::TWO_FA_ENABLED,
self::TWO_FA_KEY,
Expand Down
6 changes: 3 additions & 3 deletions backend/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
},
"prefer-stable": true,
"support": {
"wiki": "https://docs.mythical.systems",
"wiki": "https://www.mythical.systems",
"chat": "https://discord.mythical.systems",
"issues": "https://github.com/mythicalltd/mythicalcore/issues",
"docs": "https://docs.mythical.systems",
"issues": "https://github.com/mythicalltd/mythicalldash/issues",
"docs": "https://www.mythical.systems",
"forum": "https://discord.mythical.systems"
},
"funding": [
Expand Down
6 changes: 5 additions & 1 deletion backend/public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,12 @@
define('TELEMETRY', true);
define('APP_VERSION', '4.0.0.0-dev');
define('APP_UPSTREAM', 'github.com/mythicalltd/mythicaldash');
define('RATE_LIMIT', 50);

if (APP_DEBUG) {
define('RATE_LIMIT', 500000);
} else {
define('RATE_LIMIT', 50);
}
/**
* Require the kernel.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE `mythicalclient_users` ADD `support_pin` TEXT NULL DEFAULT NULL AFTER `verified`;
27 changes: 27 additions & 0 deletions frontend/src/components/client/Dashboard/Main/BillingInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<CardComponent>
<h2 class="text-white text-lg font-semibold mb-4">Billing Summary</h2>
<div class="space-y-2 text-sm">
<div class="flex justify-between">
<span class="text-purple-200">Current Balance:</span>
<span class="text-white font-medium">$250.00</span>
</div>
<div class="flex justify-between">
<span class="text-purple-200">Next Invoice:</span>
<span class="text-white font-medium">$120.00</span>
</div>
<div class="flex justify-between">
<span class="text-purple-200">Due Date:</span>
<span class="text-white font-medium">15 Jul 2023</span>
</div>
</div>
<RouterLink to="/billing"
class="mt-4 block w-full px-4 py-2 bg-purple-600 hover:bg-purple-500 text-white rounded transition-colors text-center text-sm">
View Invoices
</RouterLink>
</CardComponent>
</template>
<script lang="ts" setup>
import CardComponent from '@/components/client/ui/Card/CardComponent.vue';
</script>
25 changes: 25 additions & 0 deletions frontend/src/components/client/Dashboard/Main/Header.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-white mb-2">My Dashboard</h1>
<div class="text-purple-200 text-sm">
<RouterLink to="/" class="hover:text-white transition-colors">Portal Home</RouterLink>
<span class="mx-2">/</span>
<span>Client Area</span>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="text-right">
<p class="text-purple-200 text-sm">Welcome back,</p>
<p class="text-white font-semibold">{{ Session.getInfo('first_name') }}</p>
</div>
<img :src="`${Session.getInfo('avatar')}?height=40&width=40`" alt="Profile"
class="w-10 h-10 rounded-full border-2 border-purple-500" />
</div>
</div>
</template>
<script setup lang="ts">
import Session from '@/mythicalclient/Session';
</script>
45 changes: 45 additions & 0 deletions frontend/src/components/client/Dashboard/Main/ProductList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<CardComponent>
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-semibold text-white">Your Active Products/Services</h2>
<button class="text-purple-500 hover:text-white transition-colors">
<MenuIcon class="w-5 h-5" />
</button>
</div>
<div class="space-y-4">
<div v-for="(server, index) in servers" :key="index"
class="flex items-center justify-between py-3 border-b border-purple-700 last:border-0">
<div>
<div class="font-medium text-white">{{ server.name }}</div>
<div class="text-sm text-purple-500">{{ server.hostname }}</div>
</div>
<div class="flex items-center gap-3">
<span class="px-2 py-1 bg-emerald-500/20 text-emerald-400 rounded text-xs font-medium">
Active
</span>
<button
class="px-3 py-1 bg-purple-600 hover:bg-purple-500 text-white rounded transition-colors text-sm">
Manage
</button>
</div>
</div>
</div>
</CardComponent>

</template>
<script setup lang="ts">
import CardComponent from '@/components/client/ui/Card/CardComponent.vue';
import { ref } from 'vue';
import { MenuIcon } from 'lucide-vue-next';
const servers = ref([
{
name: 'Storage Root Server Frankfurt - Storage KVM S',
hostname: 'backup2.mythical.systems',
},
{
name: 'Storage Root Server Frankfurt - Storage KVM S',
hostname: 'backup.mythical.systems',
},
]);
</script>
22 changes: 22 additions & 0 deletions frontend/src/components/client/Dashboard/Main/Stats.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<div class="grid grid-cols-3 gap-4">
<CardComponent v-for="(stat, index) in stats" :key="index">
<component :is="stat.icon" class="w-6 h-6 text-purple-500 mb-2" />
<div class="text-3xl font-bold text-white mb-1">{{ stat.value }}</div>
<div class="text-purple-200 text-sm">{{ stat.label }}</div>
</CardComponent>
</div>
</template>
<script lang="ts" setup>
import CardComponent from '@/components/client/ui/Card/CardComponent.vue';
import {
Server as ServerIcon,
FileText as FileTextIcon,
Ticket as TicketIcon,
} from 'lucide-vue-next';
const stats = [
{ icon: ServerIcon, value: '2', label: 'Services' },
{ icon: FileTextIcon, value: '0', label: 'Unpaid Invoices' },
{ icon: TicketIcon, value: '1', label: 'Tickets' },
];
</script>
78 changes: 78 additions & 0 deletions frontend/src/components/client/Dashboard/Main/SupportPin.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<template>
<!-- Support PIN -->
<CardComponent>
<h2 class="text-purple-200 text-sm font-medium mb-2">{{ t('Components.SupportPin.title') }}</h2>
<div class="flex items-center gap-2">
<span class="text-emerald-400 text-2xl font-mono font-bold">{{ Session.getInfo('support_pin') }}</span>
<button @click="copyPin" class="text-purple-500 hover:text-white transition-colors">
<CopyIcon class="w-4 h-4" />
</button>
<button @click="resetPin" class="text-purple-500 hover:text-white transition-colors">
<RefreshCcwIcon class="w-4 h-4 transition-transform duration-500 hover:rotate-180" />
</button>

</div>
</CardComponent>
</template>
<script setup lang="ts">
import CardComponent from '@/components/client/ui/Card/CardComponent.vue';
import Auth from '@/mythicalclient/Auth';
import Session from '@/mythicalclient/Session';
import Swal from 'sweetalert2';
import { useI18n } from 'vue-i18n';
import {
RefreshCcw as RefreshCcwIcon,
Copy as CopyIcon,
} from 'lucide-vue-next';
const { t } = useI18n();
const copyPin = async () => {
const pin = Session.getInfo('support_pin');
try {
if (!navigator.clipboard) {
const textArea = document.createElement('textarea')
textArea.value = pin
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy')
document.body.removeChild(textArea)
} else {
await navigator.clipboard.writeText(pin)
}
Swal.fire({
icon: 'success',
title: t('Components.Global.Navigation.Copy.Title'),
text: t('Components.Global.Navigation.Copy.Success'),
footer: t('Components.Global.Navigation.Copy.Footer'),
})
} catch (err) {
console.error('Failed to copy command to clipboard', err)
}
};
const resetPin = async () => {
try {
const ping = await Auth.resetPin();
const pinElement = document.querySelector('span.text-emerald-400');
if (pinElement) {
pinElement.textContent = ping.toString();
Swal.fire({
title: t('Components.SupportPin.alerts.success.title'),
text: t('Components.SupportPin.alerts.success.pin_success'),
icon: 'success',
footer: t('Components.SupportPin.alerts.success.footer'),
});
}
} catch (error) {
console.error('Failed to reset support pin:', error);
Swal.fire({
title: t('Components.SupportPin.alerts.error.title'),
text: t('Components.SupportPin.alerts.error.generic'),
icon: 'error',
footer: t('Components.SupportPin.alerts.error.footer'),
});
}
};
</script>
40 changes: 40 additions & 0 deletions frontend/src/components/client/Dashboard/Main/TicketList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<!-- Recent Tickets -->
<CardComponent>
<h2 class="text-lg font-semibold text-white mb-4">Recent Tickets</h2>
<div class="space-y-3">
<div v-for="ticket in recentTickets" :key="ticket.id"
class="flex items-center justify-between py-2 border-b border-purple-700 last:border-0">
<div>
<div class="font-medium text-white">{{ ticket.title }}</div>
<div class="text-sm text-purple-500">{{ ticket.date }}</div>
</div>
<span :class="[
'px-2 py-1 rounded text-xs font-medium',
ticket.status === 'Open'
? 'bg-yellow-500/20 text-yellow-400'
: 'bg-emerald-500/20 text-emerald-400',
]">
{{ ticket.status }}
</span>
</div>
</div>
<RouterLink to="/support"
class="mt-4 block w-full px-4 py-2 bg-purple-600 hover:bg-purple-500 text-white rounded transition-colors text-center text-sm">
View All Tickets
</RouterLink>
</CardComponent>

</template>

<script setup lang="ts">
import CardComponent from '@/components/client/ui/Card/CardComponent.vue';
import { ref } from 'vue';
const recentTickets = ref([
{ id: 1, title: 'Server Performance Issue', date: '2023-07-01', status: 'Open' },
{ id: 2, title: 'Billing Inquiry', date: '2023-06-28', status: 'Closed' },
{ id: 3, title: 'Domain Transfer Request', date: '2023-06-25', status: 'Open' },
]);
</script>
Loading

0 comments on commit fdc99e7

Please sign in to comment.