Browse Source

Add channel description and info page

master
JustAnotherArchivist 3 years ago
parent
commit
311300283b
2 changed files with 24 additions and 3 deletions
  1. +2
    -0
      config.example.toml
  2. +22
    -3
      irclog.py

+ 2
- 0
config.example.toml View File

@@ -50,3 +50,5 @@
#hidden = false
# Keys of other channels that should be searched in addition to this one when a query is sent against it. If auth is required on another channel referenced here, it must be equal to this channel's.
#extrasearchchannels = []
# A description (string) of the channel, will be shown on /path if specified
#description =

+ 22
- 3
irclog.py View File

@@ -165,7 +165,7 @@ class Config(dict):
raise InvalidConfig(f'Invalid channel key {key!r}')
if not isinstance(channel, collections.abc.Mapping):
raise InvalidConfig(f'Invalid channel for {key!r}')
if any(x not in ('ircchannel', 'path', 'auth', 'active', 'hidden', 'extrasearchchannels') for x in channel):
if any(x not in ('ircchannel', 'path', 'auth', 'active', 'hidden', 'extrasearchchannels', 'description') for x in channel):
raise InvalidConfig(f'Unknown key(s) found in channel {key!r}')

if 'ircchannel' not in channel:
@@ -223,6 +223,11 @@ class Config(dict):
raise InvalidConfig(f'Invalid channel {key!r} extrasearchchannels: cannot refer to self')
# Validation of the values is performed after reading everything

if 'description' not in channel:
channel['description'] = None
if not isinstance(channel['description'], str) and channel['description'] is not None:
raise InvalidConfig(f'Invalid channel {key!r} description: must be a str or None')

# extrasearchchannels validation after reading all channels
for key, channel in obj['channels'].items():
if any(x not in obj['channels'] for x in channel['extrasearchchannels']):
@@ -806,11 +811,12 @@ class WebServer:
def __init__(self, config):
self.config = config

self._paths = {} # '/path' => ('#channel', auth, hidden, extrasearchpaths) where auth is either False (no authentication) or the HTTP header value for basic auth
self._paths = {} # '/path' => ('#channel', auth, hidden, extrasearchpaths, description) where auth is either False (no authentication) or the HTTP header value for basic auth

self._app = aiohttp.web.Application()
self._app.add_routes([
aiohttp.web.get('/', self.get_homepage),
aiohttp.web.get(r'/{path:[^/]+}', functools.partial(self._channel_handler, handler = self.get_channel_info)),
aiohttp.web.get(r'/{path:[^/]+}/{date:\d{4}-\d{2}-\d{2}}', functools.partial(self._channel_handler, handler = self.get_log)),
aiohttp.web.get(r'/{path:[^/]+}/{date:today}', functools.partial(self._channel_handler, handler = self.get_log)),
aiohttp.web.get('/{path:[^/]+}/search', functools.partial(self._channel_handler, handler = self.search)),
@@ -824,7 +830,8 @@ class WebServer:
channel['ircchannel'],
f'Basic {base64.b64encode(channel["auth"].encode("utf-8")).decode("utf-8")}' if channel['auth'] else False,
channel['hidden'],
[config['channels'][otherchannel]['path'] for otherchannel in channel['extrasearchchannels']]
[config['channels'][otherchannel]['path'] for otherchannel in channel['extrasearchchannels']],
channel['description'],
) for channel in config['channels'].values()}
needRebind = self.config['web'] != config['web'] #TODO only if there are changes to web.host or web.port; everything else can be updated without rebinding
self.config = config
@@ -870,6 +877,18 @@ class WebServer:
lines.append(f'{"(PW) " if auth else ""}<a href="/{html.escape(path)}/today">{html.escape(channel)}</a> (<a href="/{html.escape(path)}/search">search</a>)')
return aiohttp.web.Response(text = f'<!DOCTYPE html><html lang="en"><head><title>IRC logs</title></head><body>{"<br />".join(lines)}</body></html>', content_type = 'text/html')

async def get_channel_info(self, request):
self.logger.info(f'Received request {id(request)} from {request.remote!r} for {request.path!r}')
return aiohttp.web.Response(
text = ''.join([
'<!DOCTYPE html><html lang="en">',
f'<head><title>{html.escape(self._paths[request.match_info["path"]][0])}</title></head>',
f'<body>{html.escape(self._paths[request.match_info["path"]][4] or "")}</body>',
'</html>'
]),
content_type = 'text/html'
)

def _file_iter_with_path(self, fn, path):
# Open fn, iterate over its lines yielding (path, line) tuples
with open(fn, 'r') as fp:


Loading…
Cancel
Save