Skip to content

Commit

Permalink
Adds several improvements and updates
Browse files Browse the repository at this point in the history
- Removes setup.py in favor of pyproject.toml.
- Upgrades bootstrap to version 5.2.3.
- Replace jquery functions with pure js.
- Removes all external dependencies that were loaded from the HTML, now
  sysdweb can run in a completely isolated environment.
- Fixes a problem that caused the journal not to be displayed in the
  systemd user units.
  • Loading branch information
ogarcia committed Apr 10, 2023
1 parent 8d5fb08 commit 0af54e6
Show file tree
Hide file tree
Showing 29 changed files with 274 additions and 593 deletions.
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: ogarcia
28 changes: 28 additions & 0 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:
name: Upload to PyPi
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install build and publish dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python -m build --sdist --no-isolation
twine upload dist/*
8 changes: 0 additions & 8 deletions MANIFEST.in

This file was deleted.

23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ git clone https://github.com/ogarcia/sysdweb.git
virtualenv3 ./sysdweb-venv
source ./sysdweb-venv/bin/activate
cd sysdweb
pip install -r requirements.txt
python setup.py install
pip install .
```

### From pypi
Expand Down Expand Up @@ -45,7 +44,7 @@ Once you have configured sysdweb, simply run.
sysdweb
```

By default sysdweb listen in 10080 port to 127.0.0.1, you can change listen
By default sysdweb listen in 10088 port to 127.0.0.1, you can change listen
port and address with `-p` and `-l` or via environment variables.

```sh
Expand Down Expand Up @@ -113,17 +112,17 @@ file of `<service>` unit. You can specify the number of lines by this way.
/api/v1/<service>/journal/200
```

In the example defined above all valid enpoins are.
In the example defined above all valid endpoints are.

```
http://127.0.0.1:10080/api/v1/ngx/start
http://127.0.0.1:10080/api/v1/ngx/stop
http://127.0.0.1:10080/api/v1/ngx/restart
http://127.0.0.1:10080/api/v1/ngx/reload
http://127.0.0.1:10080/api/v1/ngx/reloadorrestart
http://127.0.0.1:10080/api/v1/ngx/status
http://127.0.0.1:10080/api/v1/ngx/journal
http://127.0.0.1:10080/api/v1/ngx/journal/<number>
http://127.0.0.1:10088/api/v1/ngx/start
http://127.0.0.1:10088/api/v1/ngx/stop
http://127.0.0.1:10088/api/v1/ngx/restart
http://127.0.0.1:10088/api/v1/ngx/reload
http://127.0.0.1:10088/api/v1/ngx/reloadorrestart
http://127.0.0.1:10088/api/v1/ngx/status
http://127.0.0.1:10088/api/v1/ngx/journal
http://127.0.0.1:10088/api/v1/ngx/journal/<number>
```

[1]: https://aur.archlinux.org/packages/sysdweb/
55 changes: 55 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "sysdweb"
version = "1.1.5"
description = "Control systemd services through Web or REST API"
readme = "README.md"
requires-python = ">=3.10"
license = {file = "LICENSE"}
keywords = ["REST API", "systemd"]
authors = [
{name = "Óscar García Amor", email = "[email protected]"}
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Bottle",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Natural Language :: English",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Topic :: System",
"Topic :: Utilities"
]
dependencies = [
"bottle ~= 0.12.25",
"dbus-python ~= 1.3.2",
"python-pam ~= 2.0.2",
"systemd-python"
]

[tool.setuptools.packages.find]
include = ["sysdweb*"]

[tool.setuptools.package-data]
"*" = [
"sysdweb.conf"
]
sysdweb = [
"templates/static/css/*",
"templates/static/img/*",
"templates/static/js/*",
"templates/views/*"
]

[project.scripts]
sysdweb = "sysdweb.main:main"

[project.urls]
"Bug Reports" = "https://github.com/ogarcia/sysdweb/issues"
"Source" = "https://github.com/ogarcia/sysdweb"
4 changes: 0 additions & 4 deletions requirements.txt

This file was deleted.

2 changes: 1 addition & 1 deletion sysdweb.py → run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2016-2018 Óscar García Amor <[email protected]>
# Copyright © 2016-2023 Óscar García Amor <[email protected]>
#
# Distributed under terms of the GNU GPLv3 license.

Expand Down
65 changes: 0 additions & 65 deletions setup.py

This file was deleted.

4 changes: 2 additions & 2 deletions sysdweb.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# Entries consist of:
# - [label]. It maches with URL /api/v1/<label>.
# - title. Custom description of service for page
# - title. Custom description of service for web page
# - unit. systemd unit name (with or without .service)
# All entries are mandatory
#
Expand All @@ -28,7 +28,7 @@ scope = system
# will be used. Take note that you can pass this values via environment vars
# or args and they prevail over this configuration.
#host = 127.0.0.1
#port = 10080
#port = 10088

# sysdweb uses PAM to users auth, if you are running it in system mode you
# can define here what users and groups have access. If you leave this
Expand Down
15 changes: 0 additions & 15 deletions sysdweb/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2016-2018 Óscar García Amor <[email protected]>
#
# Distributed under terms of the GNU GPLv3 license.

NAME = 'sysdweb'
VERSION = '1.1.4'
AUTHOR_NAME = 'Óscar García Amor'
AUTHOR_EMAIL = '[email protected]'
DESCRIPTION = 'Control systemd services through Web or REST API'
KEYWORDS = 'systemd web api easy'
URL = 'https://github.com/ogarcia/sysdweb'
LICENSE = 'GPLv3'
96 changes: 49 additions & 47 deletions sysdweb/config.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2016-2018 Óscar García Amor <[email protected]>
# Copyright © 2016-2023 Óscar García Amor <[email protected]>
#
# Distributed under terms of the GNU GPLv3 license.

Expand All @@ -12,85 +10,89 @@
import os
import pwd

def checkConfig(file=None):
CONFIG_FILE_NAME = 'sysdweb.conf'
LOCAL_CONFIG_FILE = os.path.join('.', CONFIG_FILE_NAME)
SYSTEM_CONFIG_FILE = os.path.join('/etc', CONFIG_FILE_NAME)

logger = logging.getLogger(__name__)

def _get_config_file():
xdg_config_home = os.environ.get('XDG_CONFIG_HOME', os.path.join(os.path.expanduser('~'), '.config'))
logger.debug(f'XDG_CONFIG_HOME: \'{xdg_config_home}\'.')
user_config_file = os.path.join(xdg_config_home, 'sysdweb', CONFIG_FILE_NAME)
logger.debug(f'User config file: \'{user_config_file}\'.')
for config_file in [LOCAL_CONFIG_FILE, user_config_file, SYSTEM_CONFIG_FILE]:
if os.access(config_file, os.R_OK):
logger.debug(f'Config file found: \'{config_file}\'.')
return config_file
raise SystemExit('No config file found.')

def configure(config, config_file=None):
"""
Parse config and discards errors
"""
logger = logging.getLogger('sysdweb.checkConfig')
if file != None:
if os.access(file, os.R_OK):
config_file = [file]
else:
raise SystemExit('Cannot read config file \'{}\'.'.format(file))
if config_file is None:
# Try to get one of default config locations
config_file = _get_config_file()
else:
config_files = [ './sysdweb.conf',
os.path.join(os.path.expanduser('~'), '.config/sysdweb/sysdweb.conf'),
'/etc/sysdweb.conf' ]
# Try to load one of config locations
config_file = [file for file in config_files if os.access(file, os.R_OK)]
if config_file == []:
raise SystemExit('No config file found.')
logger.info('Using config file \'{}\'.'.format(config_file[0]))
if not os.access(config_file, os.R_OK):
raise SystemExit(f'Cannot read config file \'{config_file}\'.')
logger.info(f'Using config file \'{config_file}\'.')

config = configparser.ConfigParser()
try:
config.read(config_file[0])
except Exception as e:
err = 'sysdweb config file is corrupted.\n{0}'.format(e)
raise SystemExit(err)
config.read(config_file)
except Exception as err:
raise SystemExit(f'sysdweb config file is corrupted.\n{err}')

# Configure valid users
if config.get('DEFAULT', 'scope', fallback='system') == 'system':
scope = config.get('DEFAULT', 'scope', fallback='system')
if scope == 'system':
# Get a full list of users
users = config.get('DEFAULT', 'users', fallback=None)
groups = config.get('DEFAULT', 'groups', fallback=None)

if users:
users = [user.strip() for user in users.split(',')]
else:
users = []

if groups:
groups = [group.strip() for group in groups.split(',')]
users = [] if users is None else list(map(lambda u: u.strip(), users.split(',')))
if groups is not None:
groups = map(lambda g: g.strip(), groups.split(','))
# Obtain usenames from groups
for group in groups:
try:
users.extend(grp.getgrnam(group)[3])
except KeyError:
logger.warning('Group \'{}\' not found in database, skipped.'.format(group))

logger.warning(f'Group \'{group}\' not found in database, skipped.')
# If left any user in list send to main process
if users:
if users == []:
logger.debug('Running in system mode, ALL system users are valid.')
else:
users = list(set(users)) # Remove duplicates
users.sort() # Sort alphabetically
logger.debug('Running in system mode, valid users \'{}\'.'.format(', '.join(users)))
config.set('DEFAULT', 'users', ','.join(users))
else:
logger.debug('Running in system mode, ALL system users are valid.')
else:
elif scope == 'user':
# Only current user can log in
user = pwd.getpwuid(os.getuid())[0]
logger.debug('Running in user mode, valid user \'{}\'.'.format(user))
logger.info(f'Running in user mode, valid user \'{user}\'.')
config.set('DEFAULT', 'users', user)
else:
raise SystemExit(f'Invalid scope \'{scope}\', must be \'system\' or \'user\'.')

# Read all sections to check if are correctly configurated
for section in config.sections():
if not config.get(section, 'title', fallback=None):
if config.get(section, 'title', fallback=None) is None:
config.remove_section(section)
logger.warning('Removed invalid section without title \'{}\' from config.'.format(section))
logger.warning(f'Removed invalid section without title \'{section}\' from config.')
else:
if not config.get(section, 'unit', fallback=None):
unit = config.get(section, 'unit', fallback=None)
if unit is None:
config.remove_section(section)
logger.warning('Removed invalid section without unit \'{}\' from config.'.format(section))
logger.warning(f'Removed invalid section without unit \'{section}\' from config.')
else:
unit = config.get(section, 'unit')
if not '.service' in unit:
unit = '{}.service'.format(unit)
unit = f'{unit}.service'
config.set(section, 'unit', unit)
logger.debug('Configured section \'{}\' for unit \'{}\''.format(section, unit))
logger.debug(f'Configured section \'{section}\' for unit \'{unit}\'.')

# If after check all sections no valid sections remain, exit with error
if len(config.sections()) < 1:
raise SystemExit('Error in config. No valid sections found.')

return config
config = configparser.ConfigParser()
Loading

0 comments on commit 0af54e6

Please sign in to comment.