<?php
namespace App\Controller;
use ApiPlatform\Api\IriConverterInterface;
use App\Entity\Booking;
use App\Mqtt\LockMode;
use App\Entity\MqttDoorLock;
use App\Entity\Property;
use App\Entity\User;
use App\Entity\User\UserRole;
use App\Mqtt\MqttLockClient;
use App\Repository\MqttDoorLockRepository;
use CodeInc\SpreadsheetResponse\SpreadsheetResponse;
use DateTimeImmutable;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Html;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
class SecurityController extends AbstractController {
public function __construct(
private Security $security,
private EntityManagerInterface $entityManagerInterface
) {}
/**
* @IsGranted("ROLE_ADMIN")
* @Route("/playground", name="playground")
*/
public function playground(?EntityManagerInterface $em): Response {
$properties = $em->getRepository(Property::class)->findAll();
//dump($properties);
$now = new DateTimeImmutable();
$properties = array_filter($properties, function (Property $p) use ($now): bool {
return !$p->getBookings(sprintf(
'2022-11-01'
),
Criteria::expr()->neq('blocking', 1),
Criteria::expr()->eq('clean', 1),
Criteria::expr()->lte('checkout', $now)
)->isEmpty();
});
//dump($properties);
$bookings = [];
foreach ($properties as $property) {
$bookings[$property->getSection()] = $property->getBookings('2022-11-01',
Criteria::expr()->neq('blocking', 1),
Criteria::expr()->eq('clean', 1),
Criteria::expr()->lte('checkout', $now)
)->filter(function (Booking $b): bool {
return $b->getResponsible()->getCompany() == 'AR A/S';
});
}
//dump($bookings);
$vr = new Csv();
$vr->setInputEncoding('UTF-8');
$vr->setDelimiter(',');
$vr->setEnclosure('"');
$vr->setSheetIndex(0);
$ss = $vr->loadSpreadsheetFromString($this->renderView(
'csv/orders.csv.twig',
[
'customer_number' => 10088,
'products' => json_decode(json_encode([[
'number' => 4,
'unit' => 'HUR'
], [
'number' => '14',
'unit' => 'SET'
]])),
'projects' => $properties,
'bookings' => $bookings,
]
));
//return new Response((new Html($ss))->generateHtmlAll(), 200);
//(new Xlsx($ss))->save('php://output');
return new SpreadsheetResponse($ss, 'fs');
// return new Response("", 200, [V
// 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// 'Content-Disposition' => sprintf('attachment;filename="orders-%s.xlsx"', $now->format('Y-m-d'))
// ]);
}
/**
* @Route({
* "/", "/login",
* "/bookings",
* "/bookings/checkout",
* "/bookings/checkin",
* "/bookings/invoicing",
* "/bookings/invoicing/{req}",
* "/bookings/invoicing/{req}/{date}"
* }, name="index")
*/
public function index(): Response {
return $this->render("base.html.twig");
}
private static function userObject(IriConverterInterface $ici, User $u): array {
return [
"@id" => $id = $ici->getIriFromResource($u),
"id" => $id,
"company" => $u->getCompany(),
"language" => $u?->getLanguage() ?: 'en',
"roles" => $u->getRoles()
];
}
private function failedLoginReponse(?Request $r = null): false|Response {
$user = User::cast($this->getUser());
if (!$this->isGranted('IS_AUTHENTICATED_FULLY'))
return $this->json([
"error" => [
"title" => 'Invalid login request",
"message" => check that the Content-Type header is "application/json", got: «%s».' . $r?->getContentType()
]
], Response::HTTP_BAD_REQUEST);
if (!$user || $user?->hasRole(UserRole::Disabled))
return $this->json([
"error" => [
"title" => "Invalid credentials or account is disabled.",
"message" => "Please contact your supervisor if you think this is a mistake."
]
], Response::HTTP_FORBIDDEN);
if (isset($_SERVER['APP_MAINTENANCE']) && !$user->userHasConstraint('APP_MAINTENANCE'))
return $this->json([
"error" => [
"title" => "App is in maintencance mode.",
"message" => sprintf("Need role(s): «%s», have: «%s»", join(
strripos($_SERVER['APP_MAINTENANCE'], 'any') === 0 ? ' or ' : ' and ', array_map(
fn(string $role): string => UserRole::fromCanBeShort($role)->name,
explode(',', explode(':', $_SERVER['APP_MAINTENANCE'])[1] ?? $_SERVER['APP_MAINTENANCE'])
)), join(', ', array_map(
fn(UserRole $role): string => $role->name,
$user->getRoles(true)
)))
],
], Response::HTTP_SERVICE_UNAVAILABLE);
return false;
}
/**
* @IsGranted("ROLE_USER")
* @Route("api/users/ping", name="api_ping", methods={"GET"})
*/
public function ping(IriConverterInterface $ici): Response {
if ($reponse = $this->failedLoginReponse())
return $reponse;
return $this->json(
static::userObject($ici, $this->getUser()), 200
);
}
/**
* @Route("api/users/login", name="api_login", methods={"POST"})
*/
public function login(Request $r, IriConverterInterface $ici): Response {
if ($reponse = $this->failedLoginReponse())
return $reponse;
return $this->json(
static::userObject($ici, $this->getUser()), 200
);
}
private function doorLock(int $id, MqttDoorLockRepository $mqttDoorLockRepository, LoggerInterface $logger, LockMode $mode): Response {
$lock = $mqttDoorLockRepository->find($id);
if ($lock == null)
return $this->json([
'error' => sprintf('Unknown lock: %d-%s', $id, Uuid::uuid4()->toString())
], Response::HTTP_NOT_FOUND);
try {
$client = new MqttLockClient(
$this->security->getUser(),
$this->entityManagerInterface,
$lock, $logger
);
$mode = $client->setLockMode($mode);
$client->disconnect();
return $this->json([
#'@id' => $iri->getIriFromItem($lock),
'locked' => $mode == LockMode::Locked,
'mode' => $mode->toString(),
], Response::HTTP_OK);
} catch (\Throwable $e) {
$lock->releaseAccess($logger);
}
}
/**
* @IsGranted("ROLE_USER")
* @Route("api/door/unlock/{id}", name="api_door_unlock",
* requirements={"id" = "[\d]+"},
* methods={"GET"}
* )
*/
public function unlockDoor(int $id, MqttDoorLockRepository $mqttDoorLockRepository, LoggerInterface $logger): Response {
return $this->doorLock($id, $mqttDoorLockRepository, $logger, LockMode::Unlocked);
}
/**
* @IsGranted("ROLE_USER")
* @Route("api/door/lock/{id}", name="api_door_lock",
* requirements={"id" = "[\d]+"},
* methods={"GET"}
* )
*/
public function lockDoor(int $id, MqttDoorLockRepository $mqttDoorLockRepository, LoggerInterface $logger): Response {
return $this->doorLock($id, $mqttDoorLockRepository, $logger, LockMode::Locked);
}
/**
* @IsGranted("ROLE_USER")
* @Route("api/door/status/{id}", name="api_door_status",
* requirements={"id" = "[\d]+"},
* methods={"GET"}
* )
*/
public function doorStatus(int $id, MqttDoorLockRepository $mqttDoorLockRepository, LoggerInterface $logger): Response {
$lock = $mqttDoorLockRepository->find($id);
if ($lock == null)
return $this->json([
'error' => sprintf('Unknown lock: %d-%s', $id, Uuid::uuid4()->toString())
], Response::HTTP_NOT_FOUND);
try {
$client = new MqttLockClient(
$this->security->getUser(),
$this->entityManagerInterface,
$lock, $logger
);
$mode = $client->getLockmode(true);
$client->disconnect();
return $this->json([
'locked' => $mode == LockMode::Locked,
'mode' => $mode->getValue()
], 200);
} catch (Exception $e) {
return $this->json([
'Info' => sprintf('Lock: %d-%s', $id, Uuid::uuid4()->toString())
], Response::HTTP_FOUND);
}
return $this->json([
'Info' => sprintf('Lock: %d-%s', $id, Uuid::uuid4()->toString())
], Response::HTTP_FOUND);
}
/**
* @Route({"users/logout", "api/users/logout"}, name="api_logout")
*/
public function logout() {
throw new \Exception('should not be reached');
}
}