webapp static form

edited 2023/11/24 by pedro
This commit is contained in:
jorgepastorr 2023-07-25 16:59:58 +02:00 committed by pedro
parent 7d9805869d
commit 24baf1bb70
8 changed files with 136 additions and 31 deletions

View File

@ -49,8 +49,6 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
add_form = DatabaseCreationForm add_form = DatabaseCreationForm
readonly_fields = ('account_link', 'display_users',) readonly_fields = ('account_link', 'display_users',)
filter_horizontal = ['users'] filter_horizontal = ['users']
# filter_by_account_fields = ('users',)
# list_prefetch_related = ('users',)
actions = (list_accounts, save_selected) actions = (list_accounts, save_selected)
@mark_safe @mark_safe

View File

@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2023-07-24 16:13
from django.db import migrations, models
import orchestra.core.validators
class Migration(migrations.Migration):
dependencies = [
('systemusers', '0002_webappusers'),
]
operations = [
migrations.AlterModelOptions(
name='webappusers',
options={'verbose_name': 'WebAppUser', 'verbose_name_plural': 'WebappUsers'},
),
migrations.AlterField(
model_name='webappusers',
name='home',
field=models.CharField(blank=True, help_text='name dir webapp /home/<main>/webapps/<DirName>', max_length=256, validators=[orchestra.core.validators.validate_string_dir], verbose_name='WebappDir'),
),
]

View File

@ -57,7 +57,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
list_filter = ('type', HasWebsiteListFilter, DetailListFilter) list_filter = ('type', HasWebsiteListFilter, DetailListFilter)
inlines = [WebAppOptionInline] inlines = [WebAppOptionInline]
readonly_fields = ('account_link',) readonly_fields = ('account_link',)
change_readonly_fields = ('name', 'type', 'display_websites') change_readonly_fields = ('name', 'type', 'display_websites', 'sftpuser', 'target_server')
search_fields = ('name', 'account__username', 'data', 'website__domains__name') search_fields = ('name', 'account__username', 'data', 'website__domains__name')
list_prefetch_related = ('content_set__website', 'content_set__website__domains') list_prefetch_related = ('content_set__website', 'content_set__website__domains')
plugin = AppType plugin = AppType
@ -67,6 +67,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
display_type = display_plugin_field('type') display_type = display_plugin_field('type')
@mark_safe @mark_safe
def display_websites(self, webapp): def display_websites(self, webapp):
websites = [] websites = []

View File

@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController from orchestra.contrib.orchestration import ServiceController
from . import WebAppServiceMixin from . import WebAppServiceMixin
from ..settings import WEBAPP_NEW_SERVERS
class StaticController(WebAppServiceMixin, ServiceController): class StaticController(WebAppServiceMixin, ServiceController):
""" """
@ -15,9 +15,21 @@ class StaticController(WebAppServiceMixin, ServiceController):
def save(self, webapp): def save(self, webapp):
context = self.get_context(webapp) context = self.get_context(webapp)
if context.get('target_server').name in WEBAPP_NEW_SERVERS:
self.check_webapp_dir(context)
self.set_under_construction(context)
# TODO: crea el usuario sftp
# webapp.name = webapp.sftpuser.directory.replace("webapps/", "")
# webapp.save()
else:
self.create_webapp_dir(context) self.create_webapp_dir(context)
self.set_under_construction(context) self.set_under_construction(context)
def delete(self, webapp): def delete(self, webapp):
context = self.get_context(webapp) context = self.get_context(webapp)
if context.get('target_server').name not in WEBAPP_NEW_SERVERS:
self.delete_webapp_dir(context) self.delete_webapp_dir(context)
else:
# TODO: elimina el usuario sftp
pass

View File

@ -1,16 +1,18 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.28 on 2023-07-24 16:08
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
import orchestra.core.validators
import jsonfield.fields
from django.conf import settings from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import jsonfield.fields
import orchestra.core.validators
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
('orchestration', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
@ -18,36 +20,32 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='WebApp', name='WebApp',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='name', validators=[orchestra.core.validators.validate_name], help_text='The app will be installed in %(home)s/webapps/%(app_name)s', max_length=128)), ('name', models.CharField(help_text='The app will be installed in %(home)s/webapps/%(app_name)s', max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('type', models.CharField(verbose_name='type', max_length=32, choices=[('php', 'PHP'), ('python', 'Python'), ('static', 'Static'), ('symbolic-link', 'Symbolic link'), ('webalizer', 'Webalizer'), ('wordpress-php', 'WordPress')])), ('type', models.CharField(choices=[('moodle-php', 'Moodle'), ('php', 'PHP'), ('python', 'Python'), ('static', 'Static'), ('symbolic-link', 'Symbolic link'), ('webalizer', 'Webalizer'), ('wordpress-php', 'WordPress')], max_length=32, verbose_name='type')),
('data', jsonfield.fields.JSONField(verbose_name='data', blank=True, help_text='Extra information dependent of each service.', default={})), ('data', jsonfield.fields.JSONField(blank=True, default={}, help_text='Extra information dependent of each service.', verbose_name='data')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='Account', related_name='webapps', to=settings.AUTH_USER_MODEL)), ('comments', models.TextField(blank=True, default='')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webapps', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('target_server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='webapps', to='orchestration.Server', verbose_name='Target Server')),
], ],
options={ options={
'verbose_name': 'Web App', 'verbose_name': 'Web App',
'verbose_name_plural': 'Web Apps', 'verbose_name_plural': 'Web Apps',
'unique_together': {('name', 'account')},
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
name='WebAppOption', name='WebAppOption',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(verbose_name='name', max_length=128, choices=[(None, '-------'), ('FileSystem', [('public-root', 'Public root')]), ('Process', [('timeout', 'Process timeout'), ('processes', 'Number of processes')]), ('PHP', [('enable_functions', 'Enable functions'), ('allow_url_include', 'Allow URL include'), ('allow_url_fopen', 'Allow URL fopen'), ('auto_append_file', 'Auto append file'), ('auto_prepend_file', 'Auto prepend file'), ('date.timezone', 'date.timezone'), ('default_socket_timeout', 'Default socket timeout'), ('display_errors', 'Display errors'), ('extension', 'Extension'), ('magic_quotes_gpc', 'Magic quotes GPC'), ('magic_quotes_runtime', 'Magic quotes runtime'), ('magic_quotes_sybase', 'Magic quotes sybase'), ('max_input_time', 'Max input time'), ('max_input_vars', 'Max input vars'), ('memory_limit', 'Memory limit'), ('mysql.connect_timeout', 'Mysql connect timeout'), ('output_buffering', 'Output buffering'), ('register_globals', 'Register globals'), ('post_max_size', 'Post max size'), ('sendmail_path', 'Sendmail path'), ('session.bug_compat_warn', 'Session bug compat warning'), ('session.auto_start', 'Session auto start'), ('safe_mode', 'Safe mode'), ('suhosin.post.max_vars', 'Suhosin POST max vars'), ('suhosin.get.max_vars', 'Suhosin GET max vars'), ('suhosin.request.max_vars', 'Suhosin request max vars'), ('suhosin.session.encrypt', 'Suhosin session encrypt'), ('suhosin.simulation', 'Suhosin simulation'), ('suhosin.executor.include.whitelist', 'Suhosin executor include whitelist'), ('upload_max_filesize', 'Upload max filesize'), ('zend_extension', 'Zend extension')])])), ('name', models.CharField(choices=[(None, '-------'), ('FileSystem', [('public-root', 'Public root')]), ('Process', [('timeout', 'Process timeout'), ('processes', 'Number of processes')]), ('PHP', [('enable_functions', 'Enable functions'), ('disable_functions', 'Disable functions'), ('allow_url_include', 'Allow URL include'), ('allow_url_fopen', 'Allow URL fopen'), ('auto_append_file', 'Auto append file'), ('auto_prepend_file', 'Auto prepend file'), ('date.timezone', 'date.timezone'), ('default_socket_timeout', 'Default socket timeout'), ('display_errors', 'Display errors'), ('extension', 'Extension'), ('include_path', 'Include path'), ('open_basedir', 'Open basedir'), ('magic_quotes_gpc', 'Magic quotes GPC'), ('magic_quotes_runtime', 'Magic quotes runtime'), ('magic_quotes_sybase', 'Magic quotes sybase'), ('max_input_time', 'Max input time'), ('max_input_vars', 'Max input vars'), ('memory_limit', 'Memory limit'), ('mysql.connect_timeout', 'Mysql connect timeout'), ('output_buffering', 'Output buffering'), ('register_globals', 'Register globals'), ('post_max_size', 'Post max size'), ('sendmail_path', 'Sendmail path'), ('session.bug_compat_warn', 'Session bug compat warning'), ('session.auto_start', 'Session auto start'), ('safe_mode', 'Safe mode'), ('suhosin.post.max_vars', 'Suhosin POST max vars'), ('suhosin.get.max_vars', 'Suhosin GET max vars'), ('suhosin.request.max_vars', 'Suhosin request max vars'), ('suhosin.session.encrypt', 'Suhosin session encrypt'), ('suhosin.simulation', 'Suhosin simulation'), ('suhosin.executor.include.whitelist', 'Suhosin executor include whitelist'), ('upload_max_filesize', 'Upload max filesize'), ('upload_tmp_dir', 'Upload tmp dir'), ('zend_extension', 'Zend extension')])], max_length=128, verbose_name='name')),
('value', models.CharField(verbose_name='value', max_length=256)), ('value', models.CharField(max_length=256, verbose_name='value')),
('webapp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='Web application', related_name='options', to='webapps.WebApp')), ('webapp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='webapps.WebApp', verbose_name='Web application')),
], ],
options={ options={
'verbose_name': 'option', 'verbose_name': 'option',
'verbose_name_plural': 'options', 'verbose_name_plural': 'options',
'unique_together': {('webapp', 'name')},
}, },
), ),
migrations.AlterUniqueTogether(
name='webappoption',
unique_together=set([('webapp', 'name')]),
),
migrations.AlterUniqueTogether(
name='webapp',
unique_together=set([('name', 'account')]),
),
] ]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.28 on 2023-07-24 16:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('systemusers', '0003_auto_20230724_1813'),
('webapps', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='webapp',
name='sftpuser',
field=models.ForeignKey(blank=True, help_text='This option is only required for the new webservers.', null=True, on_delete=django.db.models.deletion.CASCADE, to='systemusers.WebappUsers', verbose_name='SFTP user'),
),
]

View File

@ -26,6 +26,8 @@ class WebApp(models.Model):
target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE, target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE,
verbose_name=_("Target Server"), related_name='webapps') verbose_name=_("Target Server"), related_name='webapps')
comments = models.TextField(default="", blank=True) comments = models.TextField(default="", blank=True)
sftpuser = models.ForeignKey('systemusers.WebappUsers', blank=True, null=True, on_delete=models.CASCADE ,
verbose_name=_("SFTP user"), help_text=_("This option is only required for the new webservers."))
# CMS webapps usually need a database and dbuser, with these virtual fields we tell the ORM to delete them # CMS webapps usually need a database and dbuser, with these virtual fields we tell the ORM to delete them
databases = VirtualDatabaseRelation('databases.Database') databases = VirtualDatabaseRelation('databases.Database')

View File

@ -2,14 +2,64 @@ import os
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from rest_framework import serializers from rest_framework import serializers
from orchestra.core import validators
from orchestra.plugins.forms import PluginDataForm
from orchestra.utils.python import random_ascii
from ..options import AppOption from ..options import AppOption
from ..settings import WEBAPP_NEW_SERVERS
from . import AppType from . import AppType
from .php import PHPApp, PHPAppForm, PHPAppSerializer from .php import PHPApp, PHPAppForm, PHPAppSerializer
class StaticForm(PluginDataForm):
username = forms.CharField(label=_("Username"), max_length=16,
required=False, validators=[validators.validate_name],
help_text=_("Required. 16 characters or fewer. Letters, digits and "
"@/./+/-/_ only."),
error_messages={
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
"@/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"), required=False,
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
validators=[validators.validate_password],
help_text=_("Suggestion: %s") % random_ascii(15))
password2 = forms.CharField(label=_("Password confirmation"), required=False,
widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification."))
def __init__(self, *args, **kwargs):
super(StaticForm, self).__init__(*args, **kwargs)
if self.instance.id is None:
self.fields['sftpuser'].widget = forms.HiddenInput()
else:
self.fields['username'].widget = forms.HiddenInput()
self.fields['password1'].widget = forms.HiddenInput()
self.fields['password2'].widget = forms.HiddenInput()
def clean(self):
webapp_server = self.cleaned_data.get("target_server")
sftpuser = self.cleaned_data.get('sftpuser')
if webapp_server is None:
self.add_error("target_server", _("choice some target_server"))
else:
if webapp_server.name in WEBAPP_NEW_SERVERS and sftpuser == None:
self.add_error("sftpuser", _("SFTP user is required by new webservers"))
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
msg = _("The two password fields didn't match.")
raise ValidationError(msg)
return password2
class StaticApp(AppType): class StaticApp(AppType):
name = 'static' name = 'static'
verbose_name = "Static" verbose_name = "Static"
@ -17,6 +67,7 @@ class StaticApp(AppType):
"Apache2 will be used to serve static content and execute CGI files.") "Apache2 will be used to serve static content and execute CGI files.")
icon = 'orchestra/icons/apps/Static.png' icon = 'orchestra/icons/apps/Static.png'
option_groups = (AppOption.FILESYSTEM,) option_groups = (AppOption.FILESYSTEM,)
form = StaticForm
def get_directive(self): def get_directive(self):
return ('static', self.instance.get_path()) return ('static', self.instance.get_path())