diff --git a/apps/assets/migrations/0110_auto_20221021_1506.py b/apps/assets/migrations/0110_auto_20221021_1506.py new file mode 100644 index 000000000..039f7d179 --- /dev/null +++ b/apps/assets/migrations/0110_auto_20221021_1506.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.14 on 2022-10-21 07:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0109_auto_20221019_2040'), + ] + + operations = [ + migrations.AddField( + model_name='account', + name='connectivity', + field=models.CharField(choices=[('unknown', 'Unknown'), ('ok', 'Ok'), ('failed', 'Failed')], default='unknown', max_length=16, verbose_name='Connectivity'), + ), + migrations.AddField( + model_name='account', + name='date_verified', + field=models.DateTimeField(null=True, verbose_name='Date verified'), + ), + ] diff --git a/apps/assets/models/account.py b/apps/assets/models/account.py index 70351a726..70ddc81b0 100644 --- a/apps/assets/models/account.py +++ b/apps/assets/models/account.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords from common.utils import lazyproperty -from .base import BaseAccount +from .base import BaseAccount, AbsConnectivity __all__ = ['Account', 'AccountTemplate'] @@ -37,7 +37,7 @@ class AccountHistoricalRecords(HistoricalRecords): return super().fields_included(model) -class Account(BaseAccount): +class Account(AbsConnectivity, BaseAccount): class InnerAccount(models.TextChoices): INPUT = '@INPUT', '@INPUT' USER = '@USER', '@USER' diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index ae3c99b60..5355d0035 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -86,7 +86,7 @@ class Protocol(models.Model): return '{}/{}'.format(self.name, self.port) -class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): +class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, verbose_name=_('Name')) address = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True) @@ -100,6 +100,7 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels")) comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) info = models.JSONField(verbose_name='Info', default=dict, blank=True) + objects = AssetManager.from_queryset(AssetQuerySet)() def __str__(self): diff --git a/apps/common/db/models.py b/apps/common/db/models.py index bac1f1b52..7ff88a877 100644 --- a/apps/common/db/models.py +++ b/apps/common/db/models.py @@ -87,10 +87,6 @@ class JMSBaseModel(BaseCreateUpdateModel): abstract = True -def concated_display(name1, name2): - return Concat(F(name1), Value('('), F(name2), Value(')')) - - def output_as_string(field_name): return ExpressionWrapper(F(field_name), output_field=models.CharField()) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 849577c24..d5c183d17 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1102,7 +1102,7 @@ msgstr "立刻推送" #: assets/serializers/account/account.py:18 msgid "Has secret" -msgstr "有密码" +msgstr "存在密码" #: assets/serializers/account/account.py:25 msgid "Account template not found" diff --git a/apps/ops/apps.py b/apps/ops/apps.py index 819a23002..7956a5dc1 100644 --- a/apps/ops/apps.py +++ b/apps/ops/apps.py @@ -15,4 +15,5 @@ class OpsConfig(AppConfig): from .celery import signal_handler from . import signal_handlers from . import notifications + from . import tasks super().ready() diff --git a/apps/rbac/builtin.py b/apps/rbac/builtin.py index 77ec14cf1..08e54374c 100644 --- a/apps/rbac/builtin.py +++ b/apps/rbac/builtin.py @@ -164,7 +164,7 @@ class BuiltinRole: @classmethod def sync_to_db(cls, show_msg=False): roles = cls.get_roles() - print("\n\tUpdate builtin roles") + print(" - Update builtin roles") for pre_role in roles.values(): role, created = pre_role.update_or_create_role() diff --git a/apps/rbac/signal_handlers.py b/apps/rbac/signal_handlers.py index a158b7e6a..37eed870a 100644 --- a/apps/rbac/signal_handlers.py +++ b/apps/rbac/signal_handlers.py @@ -11,7 +11,7 @@ def after_migrate_update_builtin_role_permissions(sender, app_config, **kwargs): # 最后一个 app migrations 后执行, 更新内置角色的权限 last_app = list(apps.get_app_configs())[-1] if app_config.name == last_app.name: - print("\tAfter migration, update builtin role permissions") + print("\nAfter migration, update builtin role permissions") BuiltinRole.sync_to_db() diff --git a/apps/terminal/migrations/0054_auto_20221021_1433.py b/apps/terminal/migrations/0054_auto_20221021_1433.py new file mode 100644 index 000000000..830ef897c --- /dev/null +++ b/apps/terminal/migrations/0054_auto_20221021_1433.py @@ -0,0 +1,96 @@ +# Generated by Django 3.2.14 on 2022-10-21 06:33 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0053_auto_20220830_1244'), + ] + + operations = [ + migrations.CreateModel( + name='Applet', + fields=[ + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('version', models.CharField(max_length=16, verbose_name='Version')), + ('type', models.CharField(choices=[('app', 'App'), ('web', 'Web')], max_length=16, verbose_name='Type')), + ('icon', models.ImageField(upload_to='applet/icon', verbose_name='Icon')), + ('author', models.CharField(max_length=128, verbose_name='Author')), + ('protocols', models.JSONField(default=list, verbose_name='Protocol')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='AppletProvider', + fields=[ + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('account_automation', models.BooleanField(default=False, verbose_name='Account automation')), + ('date_synced', models.DateTimeField(blank=True, null=True, verbose_name='Date synced')), + ('status', models.CharField(max_length=16, verbose_name='Status')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ProviderDeployment', + fields=[ + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('status', models.CharField(max_length=16, verbose_name='Status')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='terminal.appletprovider', verbose_name='Provider')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='AppletPublication', + fields=[ + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('status', models.CharField(max_length=16, verbose_name='Status')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('applet', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='terminal.applet', verbose_name='Applet')), + ('provider', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='terminal.appletprovider', verbose_name='Provider')), + ], + options={ + 'unique_together': {('applet', 'provider')}, + }, + ), + migrations.AddField( + model_name='appletprovider', + name='applets', + field=models.ManyToManyField(through='terminal.AppletPublication', to='terminal.Applet', verbose_name='Applet'), + ), + migrations.AddField( + model_name='appletprovider', + name='asset', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='assets.asset', verbose_name='Asset'), + ), + ] diff --git a/apps/terminal/models/__init__.py b/apps/terminal/models/__init__.py index be079721d..268727394 100644 --- a/apps/terminal/models/__init__.py +++ b/apps/terminal/models/__init__.py @@ -1,9 +1,3 @@ -from .command import * from .session import * -from .status import * -from .storage import * -from .task import * -from .terminal import * -from .sharing import * -from .replay import * -from .endpoint import * +from .component import * +from .applet import * diff --git a/apps/terminal/models/applet/__init__.py b/apps/terminal/models/applet/__init__.py new file mode 100644 index 000000000..daef278e0 --- /dev/null +++ b/apps/terminal/models/applet/__init__.py @@ -0,0 +1,2 @@ +from .applet import * +from .provider import * diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py new file mode 100644 index 000000000..4fc16819f --- /dev/null +++ b/apps/terminal/models/applet/applet.py @@ -0,0 +1,33 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from common.db.models import JMSBaseModel + + +__all__ = ['Applet', 'AppletPublication'] + + +class Applet(JMSBaseModel): + class Type(models.TextChoices): + app = 'app', _('App') + web = 'web', _('Web') + name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True) + version = models.CharField(max_length=16, verbose_name=_('Version')) + type = models.CharField(max_length=16, choices=Type.choices, verbose_name=_('Type')) + icon = models.ImageField(upload_to='applet/icon', verbose_name=_('Icon')) + author = models.CharField(max_length=128, verbose_name=_('Author')) + protocols = models.JSONField(default=list, verbose_name=_('Protocol')) + comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) + + def __str__(self): + return self.name + + +class AppletPublication(JMSBaseModel): + applet = models.ForeignKey('Applet', on_delete=models.PROTECT, verbose_name=_('Applet')) + provider = models.ForeignKey('AppletProvider', on_delete=models.PROTECT, verbose_name=_('Provider')) + status = models.CharField(max_length=16, verbose_name=_('Status')) + comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) + + class Meta: + unique_together = ('applet', 'provider') diff --git a/apps/terminal/models/applet/provider.py b/apps/terminal/models/applet/provider.py new file mode 100644 index 000000000..dac90850d --- /dev/null +++ b/apps/terminal/models/applet/provider.py @@ -0,0 +1,31 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from celery import current_app + +from common.db.models import JMSBaseModel + + +__all__ = ['AppletProvider', 'ProviderDeployment'] + + +class AppletProvider(JMSBaseModel): + name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True) + asset = models.ForeignKey('assets.Asset', on_delete=models.PROTECT, verbose_name=_('Asset')) + comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) + account_automation = models.BooleanField(default=False, verbose_name=_('Account automation')) + date_synced = models.DateTimeField(null=True, blank=True, verbose_name=_('Date synced')) + status = models.CharField(max_length=16, verbose_name=_('Status')) + applets = models.ManyToManyField( + 'Applet', verbose_name=_('Applet'), + through='AppletPublication', through_fields=('provider', 'applet'), + ) + + +class ProviderDeployment(JMSBaseModel): + provider = models.ForeignKey('AppletProvider', on_delete=models.CASCADE, verbose_name=_('Provider')) + status = models.CharField(max_length=16, verbose_name=_('Status')) + comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) + + def install(self): + pass diff --git a/apps/terminal/models/component/__init__.py b/apps/terminal/models/component/__init__.py new file mode 100644 index 000000000..b136a5da3 --- /dev/null +++ b/apps/terminal/models/component/__init__.py @@ -0,0 +1,5 @@ +from .terminal import * +from .task import * +from .endpoint import * +from .status import * +from .storage import * diff --git a/apps/terminal/models/endpoint.py b/apps/terminal/models/component/endpoint.py similarity index 99% rename from apps/terminal/models/endpoint.py rename to apps/terminal/models/component/endpoint.py index f823f6c90..dac2b1f6d 100644 --- a/apps/terminal/models/endpoint.py +++ b/apps/terminal/models/component/endpoint.py @@ -1,6 +1,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.core.validators import MinValueValidator, MaxValueValidator + from common.db.models import JMSBaseModel from common.db.fields import PortField from common.utils.ip import contains_ip diff --git a/apps/terminal/models/status.py b/apps/terminal/models/component/status.py similarity index 100% rename from apps/terminal/models/status.py rename to apps/terminal/models/component/status.py diff --git a/apps/terminal/models/storage.py b/apps/terminal/models/component/storage.py similarity index 98% rename from apps/terminal/models/storage.py rename to apps/terminal/models/component/storage.py index 555c44aea..7cab98058 100644 --- a/apps/terminal/models/storage.py +++ b/apps/terminal/models/component/storage.py @@ -1,22 +1,21 @@ from __future__ import unicode_literals - import copy import os - from importlib import import_module import jms_storage from django.db import models from django.utils.translation import ugettext_lazy as _ from django.conf import settings + from common.mixins import CommonModelMixin from common.utils import get_logger from common.db.fields import EncryptJsonDictTextField from common.utils.timezone import local_now_date_display from terminal.backends import TYPE_ENGINE_MAPPING from .terminal import Terminal -from .command import Command -from .. import const +from ..session.command import Command +from terminal import const logger = get_logger(__file__) diff --git a/apps/terminal/models/task.py b/apps/terminal/models/component/task.py similarity index 100% rename from apps/terminal/models/task.py rename to apps/terminal/models/component/task.py diff --git a/apps/terminal/models/terminal.py b/apps/terminal/models/component/terminal.py similarity index 97% rename from apps/terminal/models/terminal.py rename to apps/terminal/models/component/terminal.py index 4b585cfff..4a95fbd3a 100644 --- a/apps/terminal/models/terminal.py +++ b/apps/terminal/models/component/terminal.py @@ -9,9 +9,9 @@ from common.utils import get_logger from users.models import User from orgs.utils import tmp_to_root_org from .status import Status -from .. import const -from ..const import ComponentStatusChoices as StatusChoice -from .session import Session +from terminal import const +from terminal.const import ComponentStatusChoices as StatusChoice +from ..session import Session logger = get_logger(__file__) diff --git a/apps/terminal/models/session/__init__.py b/apps/terminal/models/session/__init__.py new file mode 100644 index 000000000..073c9d078 --- /dev/null +++ b/apps/terminal/models/session/__init__.py @@ -0,0 +1,4 @@ +from .command import * +from .session import * +from .replay import * +from .sharing import * diff --git a/apps/terminal/models/command.py b/apps/terminal/models/session/command.py similarity index 96% rename from apps/terminal/models/command.py rename to apps/terminal/models/session/command.py index 44edf013c..c940e855b 100644 --- a/apps/terminal/models/command.py +++ b/apps/terminal/models/session/command.py @@ -4,7 +4,7 @@ from django.db import models from django.db.models.signals import post_save from django.utils.translation import ugettext_lazy as _ -from ..backends.command.models import AbstractSessionCommand +from terminal.backends.command.models import AbstractSessionCommand class CommandManager(models.Manager): diff --git a/apps/terminal/models/replay.py b/apps/terminal/models/session/replay.py similarity index 100% rename from apps/terminal/models/replay.py rename to apps/terminal/models/session/replay.py diff --git a/apps/terminal/models/session.py b/apps/terminal/models/session/session.py similarity index 99% rename from apps/terminal/models/session.py rename to apps/terminal/models/session/session.py index 4f01bde4a..0a095a401 100644 --- a/apps/terminal/models/session.py +++ b/apps/terminal/models/session/session.py @@ -16,7 +16,7 @@ from users.models import User from orgs.mixins.models import OrgModelMixin from django.db.models import TextChoices from common.utils import get_object_or_none, lazyproperty -from ..backends import get_multi_command_storage +from terminal.backends import get_multi_command_storage class Session(OrgModelMixin): diff --git a/apps/terminal/models/sharing.py b/apps/terminal/models/session/sharing.py similarity index 100% rename from apps/terminal/models/sharing.py rename to apps/terminal/models/session/sharing.py