Browse Source

implement new room submit feature

main
ghost 11 months ago
parent
commit
7c51f3d57a
  1. 21
      .env
  2. 12
      composer.lock
  3. 5
      config/services.yaml
  4. 1
      public/css/default.css
  5. 38
      src/Controller/ModuleController.php
  6. 205
      src/Controller/RoomController.php
  7. 7
      templates/default/module/room.html.twig
  8. 2
      templates/default/module/rooms.html.twig
  9. 12
      templates/default/room/list.html.twig

21
.env

@ -19,7 +19,7 @@ APP_ENV=dev
APP_SECRET=EDIT_ME APP_SECRET=EDIT_ME
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###
APP_VERSION=1.3.1 APP_VERSION=1.4.0
APP_NAME=KevaChat APP_NAME=KevaChat
@ -61,23 +61,32 @@ APP_KEVACOIN_ROOM_NAMESPACE_DEFAULT=EDIT_ME
# Online session expiration timeout # Online session expiration timeout
APP_SESSION_ONLINE_TIMEOUT=900 APP_SESSION_ONLINE_TIMEOUT=900
# Moderators IP with extra permissions, separated with |
APP_MODERATOR_REMOTE_IP=
# Allow remotes to create new rooms (namespaces) # Allow remotes to create new rooms (namespaces)
APP_ADD_ROOM_REMOTE_IP_REGEX=/.*/ APP_ADD_ROOM_REMOTE_IP_REGEX=/.*/
# Time quota delay for new room submit ability per IP (seconds)
APP_ADD_ROOM_REMOTE_IP_DELAY=86400
# Skip access limits for banned IPs separated by |
APP_ADD_ROOM_REMOTE_IP_DENIED=
# Room name rules (for kevacoin _KEVA_NS_, max length 520)
APP_ADD_ROOM_KEVA_NS_VALUE_REGEX=/^[\w\s_-]{2,140}$/ui
# Allow remotes to create new posts (submit key/values) # Allow remotes to create new posts (submit key/values)
APP_ADD_POST_REMOTE_IP_REGEX=/.*/ APP_ADD_POST_REMOTE_IP_REGEX=/.*/
# Time quota for remote publications by IP (seconds) # Time quota delay for post publications per IP (seconds)
APP_ADD_POST_REMOTE_IP_DELAY=60 APP_ADD_POST_REMOTE_IP_DELAY=60
# Skip access limits for following IPs separated by |
APP_ADD_POST_REMOTE_IP_MODERATORS=
# Skip access limits for banned IPs separated by | # Skip access limits for banned IPs separated by |
APP_ADD_POST_REMOTE_IP_DENIED= APP_ADD_POST_REMOTE_IP_DENIED=
# Post ID rules (for kevacoin key) do not change to keep external KevaChat nodes compatibility # Post ID rules (for kevacoin key) do not change to keep external KevaChat nodes compatibility
APP_ADD_POST_KEY_REGEX=/^([\d]+)@([A-z0-9\.\:\[\]]+)$/ APP_ADD_POST_KEY_REGEX=/^([\d]+)@([A-z0-9\.\:\[\]]+)$/
# Post content rules (for kevacoin value) # Post content rules (for kevacoin value, max length 3072)
APP_ADD_POST_VALUE_REGEX=/^[\w\s\:\.\,\'\"\/\!\?\@\#\%\(\)\[\]\+\-\*\$\%]{2,3072}$/ui APP_ADD_POST_VALUE_REGEX=/^[\w\s\:\.\,\'\"\/\!\?\@\#\%\(\)\[\]\+\-\*\$\%]{2,3072}$/ui

12
composer.lock generated

@ -132,16 +132,16 @@
}, },
{ {
"name": "kevachat/kevacoin", "name": "kevachat/kevacoin",
"version": "1.4.2", "version": "1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/kevachat/kevacoin-php.git", "url": "https://github.com/kevachat/kevacoin-php.git",
"reference": "b6e5416c58d379fd197bbd6d1039a30042ea0481" "reference": "44608e688f69ff1545f2a3103c4504bfb3294831"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/kevachat/kevacoin-php/zipball/b6e5416c58d379fd197bbd6d1039a30042ea0481", "url": "https://api.github.com/repos/kevachat/kevacoin-php/zipball/44608e688f69ff1545f2a3103c4504bfb3294831",
"reference": "b6e5416c58d379fd197bbd6d1039a30042ea0481", "reference": "44608e688f69ff1545f2a3103c4504bfb3294831",
"shasum": "" "shasum": ""
}, },
"type": "library", "type": "library",
@ -156,9 +156,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/kevachat/kevacoin-php/issues", "issues": "https://github.com/kevachat/kevacoin-php/issues",
"source": "https://github.com/kevachat/kevacoin-php/tree/1.4.2" "source": "https://github.com/kevachat/kevacoin-php/tree/1.5.0"
}, },
"time": "2023-12-06T09:20:08+00:00" "time": "2023-12-09T18:55:04+00:00"
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",

5
config/services.yaml

@ -24,12 +24,15 @@ parameters:
app.kevacoin.mine.solo.url: '%env(APP_KEVACOIN_MINE_SOLO_URL)%' app.kevacoin.mine.solo.url: '%env(APP_KEVACOIN_MINE_SOLO_URL)%'
app.session.online.timeout: '%env(APP_SESSION_ONLINE_TIMEOUT)%' app.session.online.timeout: '%env(APP_SESSION_ONLINE_TIMEOUT)%'
app.add.room.remote.ip.regex: '%env(APP_ADD_ROOM_REMOTE_IP_REGEX)%' app.add.room.remote.ip.regex: '%env(APP_ADD_ROOM_REMOTE_IP_REGEX)%'
app.add.room.remote.ip.delay: '%env(APP_ADD_ROOM_REMOTE_IP_DELAY)%'
app.add.room.remote.ip.denied: '%env(APP_ADD_ROOM_REMOTE_IP_DENIED)%'
app.add.room.keva.ns.value.regex: '%env(APP_ADD_ROOM_KEVA_NS_VALUE_REGEX)%'
app.add.post.remote.ip.regex: '%env(APP_ADD_POST_REMOTE_IP_REGEX)%' app.add.post.remote.ip.regex: '%env(APP_ADD_POST_REMOTE_IP_REGEX)%'
app.add.post.remote.ip.delay: '%env(APP_ADD_POST_REMOTE_IP_DELAY)%' app.add.post.remote.ip.delay: '%env(APP_ADD_POST_REMOTE_IP_DELAY)%'
app.add.post.remote.ip.moderators: '%env(APP_ADD_POST_REMOTE_IP_MODERATORS)%'
app.add.post.remote.ip.denied: '%env(APP_ADD_POST_REMOTE_IP_DENIED)%' app.add.post.remote.ip.denied: '%env(APP_ADD_POST_REMOTE_IP_DENIED)%'
app.add.post.key.regex: '%env(APP_ADD_POST_KEY_REGEX)%' app.add.post.key.regex: '%env(APP_ADD_POST_KEY_REGEX)%'
app.add.post.value.regex: '%env(APP_ADD_POST_VALUE_REGEX)%' app.add.post.value.regex: '%env(APP_ADD_POST_VALUE_REGEX)%'
app.moderator.remote.ip: '%env(APP_MODERATOR_REMOTE_IP)%'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file

1
public/css/default.css

@ -155,6 +155,7 @@ footer > form
width: 100%; width: 100%;
} }
footer > form > input[type="text"],
footer > form > textarea footer > form > textarea
{ {
box-sizing: border-box; box-sizing: border-box;

38
src/Controller/ModuleController.php

@ -99,18 +99,27 @@ class ModuleController extends AbstractController
Request $request Request $request
): Response ): Response
{ {
$client = new \Kevachat\Kevacoin\Client( // Create rooms list
$this->getParameter('app.kevacoin.protocol'), $list = [];
$this->getParameter('app.kevacoin.host'),
$this->getParameter('app.kevacoin.port'), foreach ((array) explode('|', $this->getParameter('app.kevacoin.room.namespaces.pinned')) as $room)
$this->getParameter('app.kevacoin.username'), {
$this->getParameter('app.kevacoin.password') $list[] = $room;
); }
// Append custom valid namespace to the rooms list menu
if (!in_array($request->get('namespace'), $list) && preg_match('/^[A-z0-9]{34}$/', $request->get('namespace')))
{
$list[] = $request->get('namespace');
}
// Render the template
return $this->render( return $this->render(
'default/module/rooms.html.twig', 'default/module/rooms.html.twig',
[ [
'list' => (array) explode('|', $this->getParameter('app.kevacoin.room.namespaces.pinned')), 'list' => array_unique(
$list
),
'form' => 'form' =>
[ [
'namespace' => 'namespace' =>
@ -178,4 +187,17 @@ class ModuleController extends AbstractController
] ]
); );
} }
public function room(
Request $request
): Response
{
return $this->render(
'default/module/room.html.twig',
[
'name' => $request->get('name'),
'error' => $request->get('error')
]
);
}
} }

205
src/Controller/RoomController.php

@ -95,9 +95,9 @@ class RoomController extends AbstractController
array_multisort( array_multisort(
array_column( array_column(
$list, $list,
'name' 'total'
), ),
SORT_ASC, SORT_DESC,
$list $list
); );
@ -165,7 +165,7 @@ class RoomController extends AbstractController
$feed = []; $feed = [];
// Get pending paradise // Get pending paradise
foreach ((array) $client->kevaPending() as $pending) foreach ((array) $client->kevaPending() as $pending) // @TODO relate to this room
{ {
// Ignore pending posts from other rooms // Ignore pending posts from other rooms
if ($pending['namespace'] !== $request->get('namespace')) if ($pending['namespace'] !== $request->get('namespace'))
@ -347,7 +347,7 @@ class RoomController extends AbstractController
$this->getParameter('app.kevacoin.password') $this->getParameter('app.kevacoin.password')
); );
// Check namespace defined in config // Check namespace available on this wallet
$rooms = []; $rooms = [];
foreach ((array) $client->kevaListNamespaces() as $value) foreach ((array) $client->kevaListNamespaces() as $value)
@ -373,7 +373,7 @@ class RoomController extends AbstractController
// Ignore this rule for is moderators // Ignore this rule for is moderators
!in_array( !in_array(
$request->getClientIp(), $request->getClientIp(),
(array) explode('|', $this->getParameter('app.add.post.remote.ip.moderators')) (array) explode('|', $this->getParameter('app.moderator.remote.ip'))
) && ) &&
// Check namespace writable or user is moderator // Check namespace writable or user is moderator
@ -401,7 +401,10 @@ class RoomController extends AbstractController
[ [
'namespace' => $request->get('namespace'), 'namespace' => $request->get('namespace'),
'message' => $request->get('message'), 'message' => $request->get('message'),
'error' => $translator->trans('Access denied for this IP!') 'error' => sprintf(
$translator->trans('Access denied for host %s!'),
$request->getClientIp()
)
] ]
); );
} }
@ -414,7 +417,10 @@ class RoomController extends AbstractController
[ [
'namespace' => $request->get('namespace'), 'namespace' => $request->get('namespace'),
'message' => $request->get('message'), 'message' => $request->get('message'),
'error' => $translator->trans('Access not allowed for this IP!') 'error' => sprintf(
$translator->trans('Access restricted for host %s!'),
$request->getClientIp()
)
] ]
); );
} }
@ -445,7 +451,7 @@ class RoomController extends AbstractController
'namespace' => $request->get('namespace'), 'namespace' => $request->get('namespace'),
'message' => $request->get('message'), 'message' => $request->get('message'),
'error' => sprintf( 'error' => sprintf(
$translator->trans('Please wait for %s seconds before post new message!'), $translator->trans('Please wait %s seconds before post new message!'),
(int) $this->getParameter('app.add.post.remote.ip.delay') - (time() - $delay) (int) $this->getParameter('app.add.post.remote.ip.delay') - (time() - $delay)
) )
] ]
@ -462,7 +468,7 @@ class RoomController extends AbstractController
'message' => $request->get('message'), 'message' => $request->get('message'),
'error' => sprintf( 'error' => sprintf(
$translator->trans('Insufficient funds, wallet: %s'), $translator->trans('Insufficient funds, wallet: %s'),
$this->getParameter('app.kevacoin.mine.address') $this->getParameter('app.kevacoin.boost.address')
) )
] ]
); );
@ -510,6 +516,187 @@ class RoomController extends AbstractController
); );
} }
#[Route(
'/room/add',
name: 'room_add',
methods:
[
'POST'
]
)]
public function add(
Request $request,
TranslatorInterface $translator
): Response
{
// Check maintenance mode disabled
if ($this->getParameter('app.maintenance'))
{
return $this->redirectToRoute(
'room_namespace',
[
'namespace' => $request->get('namespace'),
'message' => $request->get('message'),
'error' => $this->getParameter('app.maintenance')
]
);
}
// Connect memcached
$memcached = new \Memcached();
$memcached->addServer(
$this->getParameter('app.memcached.host'),
$this->getParameter('app.memcached.port')
);
$memory = md5(
sprintf(
'%s.RoomController::add:add.room.remote.ip.delay:%s',
__DIR__,
$request->getClientIp(),
),
);
// Connect kevacoin
$client = new \Kevachat\Kevacoin\Client(
$this->getParameter('app.kevacoin.protocol'),
$this->getParameter('app.kevacoin.host'),
$this->getParameter('app.kevacoin.port'),
$this->getParameter('app.kevacoin.username'),
$this->getParameter('app.kevacoin.password')
);
// Trim extra spaces from room name
$name = trim(
$request->get('name')
);
// Validate room name regex
if (!preg_match($this->getParameter('app.add.room.keva.ns.value.regex'), $name))
{
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => sprintf(
$translator->trans('Room name does not match node requirements: %s'),
$this->getParameter('app.add.room.keva.ns.value.regex')
)
]
);
}
// Check room name not added before
$rooms = [];
foreach ((array) $client->kevaListNamespaces() as $value)
{
$rooms[$value['namespaceId']] = $value['displayName'];
}
if (in_array($name, $rooms))
{
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => $translator->trans('Room with same name already exists on this node!')
]
);
}
// Deny requests from banned remote hosts
if (in_array($request->getClientIp(), (array) explode('|', $this->getParameter('app.add.room.remote.ip.denied'))))
{
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => sprintf(
$translator->trans('Access denied for host %s!'),
$request->getClientIp()
)
]
);
}
// Validate remote IP regex
if (!preg_match($this->getParameter('app.add.room.remote.ip.regex'), $request->getClientIp()))
{
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => sprintf(
$translator->trans('Access restricted for host %s!'),
$request->getClientIp()
)
]
);
}
// Validate remote IP limits
if ($delay = (int) $memcached->get($memory))
{
// Error
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => sprintf(
$translator->trans('Please wait for %s seconds before add new room!'),
(int) $this->getParameter('app.add.room.remote.ip.delay') - (time() - $delay)
)
]
);
}
// Validate funds available yet
if (1 > $client->getBalance())
{
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => sprintf(
$translator->trans('Insufficient funds, wallet: %s'),
$this->getParameter('app.kevacoin.boost.address')
)
]
);
}
// Send message to DHT
if ($namespace = $client->kevaNamespace($name))
{
// Register event time
$memcached->set(
$memory,
time(),
(int) $this->getParameter('app.add.room.remote.ip.delay') // auto remove on cache expire
);
// Redirect to new room
return $this->redirectToRoute(
'room_namespace',
[
'namespace' => $namespace['namespaceId'],
'error' => null,
'message' => null
]
);
}
// Something went wrong, return error message
return $this->redirectToRoute(
'room_list',
[
'name' => $name,
'error' => $translator->trans('Internal error! Please feedback')
]
);
}
private function _post(array $data): ?object private function _post(array $data): ?object
{ {
// Validate key format allowed in settings // Validate key format allowed in settings

7
templates/default/module/room.html.twig

@ -0,0 +1,7 @@
<form name="room" action="{{ path('room_add') }}" method="post">
{% if error %}
<output name="error" for="form-room-name">{{ error }}</output>
{% endif %}
<input type="text" name="name" id="form-room-name" value="{{ name }}" placeholder="{{ 'enter new room name...' | trans }}" />
<button type="submit">{{ 'add' | trans }}</button>
</form>

2
templates/default/module/rooms.html.twig

@ -1,4 +1,4 @@
<form name="room" action="{{ path('room_index') }}" method="get"> <form name="rooms" action="{{ path('room_index') }}" method="get">
<input type="text" name="namespace" value="{{ form.namespace.value }}" placeholder="{{ 'join room by kevacoin namespace...' | trans }}" /> <input type="text" name="namespace" value="{{ form.namespace.value }}" placeholder="{{ 'join room by kevacoin namespace...' | trans }}" />
</form> </form>
{% if list %} {% if list %}

12
templates/default/room/list.html.twig

@ -43,4 +43,16 @@
</li> </li>
</ul> </ul>
{% endif %} {% endif %}
{% endblock %}
{% block footer_content %}
{{
render(
controller(
'App\\Controller\\ModuleController::room',
{
request: request
}
)
)
}}
{% endblock %} {% endblock %}
Loading…
Cancel
Save