mirror of https://github.com/openspug/spug
336 lines
14 KiB
Python
336 lines
14 KiB
Python
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
|
|
# Copyright: (c) <spug.dev@gmail.com>
|
|
# Released under the AGPL-3.0 License.
|
|
from django.views.generic import View
|
|
from django.db.models import F
|
|
from libs import json_response, JsonParser, Argument, auth
|
|
from apps.app.models import Deploy, App
|
|
from apps.config.models import *
|
|
import json
|
|
import re
|
|
|
|
|
|
class EnvironmentView(View):
|
|
def get(self, request):
|
|
query = {}
|
|
if not request.user.is_supper:
|
|
query['id__in'] = request.user.deploy_perms['envs']
|
|
envs = Environment.objects.filter(**query)
|
|
return json_response(envs)
|
|
|
|
@auth('config.env.add|config.env.edit')
|
|
def post(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, required=False),
|
|
Argument('name', help='请输入环境名称'),
|
|
Argument('key', help='请输入唯一标识符'),
|
|
Argument('desc', required=False)
|
|
).parse(request.body)
|
|
if error is None:
|
|
if not re.fullmatch(r'\w+', form.key, re.ASCII):
|
|
return json_response(error='标识符必须为字母、数字和下划线的组合')
|
|
|
|
env = Environment.objects.filter(key=form.key).first()
|
|
if env and env.id != form.id:
|
|
return json_response(error=f'唯一标识符 {form.key} 已存在,请更改后重试')
|
|
if form.id:
|
|
Environment.objects.filter(pk=form.id).update(**form)
|
|
else:
|
|
env = Environment.objects.create(created_by=request.user, **form)
|
|
env.sort_id = env.id
|
|
env.save()
|
|
return json_response(error=error)
|
|
|
|
@auth('config.env.edit')
|
|
def patch(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='参数错误'),
|
|
Argument('sort', filter=lambda x: x in ('up', 'down'), required=False)
|
|
).parse(request.body)
|
|
if error is None:
|
|
env = Environment.objects.filter(pk=form.id).first()
|
|
if not env:
|
|
return json_response(error='未找到指定环境')
|
|
if form.sort:
|
|
if form.sort == 'up':
|
|
tmp = Environment.objects.filter(sort_id__gt=env.sort_id).last()
|
|
else:
|
|
tmp = Environment.objects.filter(sort_id__lt=env.sort_id).first()
|
|
if tmp:
|
|
tmp.sort_id, env.sort_id = env.sort_id, tmp.sort_id
|
|
tmp.save()
|
|
env.save()
|
|
return json_response(error=error)
|
|
|
|
@auth('config.env.del')
|
|
def delete(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='请指定操作对象')
|
|
).parse(request.GET)
|
|
if error is None:
|
|
if Deploy.objects.filter(env_id=form.id).exists():
|
|
return json_response(error='该环境已关联了发布配置,请删除相关发布配置后再尝试删除')
|
|
# auto delete configs
|
|
Config.objects.filter(env_id=form.id).delete()
|
|
ConfigHistory.objects.filter(env_id=form.id).delete()
|
|
Environment.objects.filter(pk=form.id).delete()
|
|
return json_response(error=error)
|
|
|
|
|
|
class ServiceView(View):
|
|
@auth('config.src.view')
|
|
def get(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, required=False)
|
|
).parse(request.GET)
|
|
if error is None:
|
|
if form.id:
|
|
service = Service.objects.get(pk=form.id)
|
|
return json_response(service)
|
|
services = Service.objects.all()
|
|
return json_response(services)
|
|
return json_response(error=error)
|
|
|
|
@auth('config.src.add|config.src.edit')
|
|
def post(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, required=False),
|
|
Argument('name', help='请输入服务名称'),
|
|
Argument('key', help='请输入唯一标识符'),
|
|
Argument('desc', required=False)
|
|
).parse(request.body)
|
|
if error is None:
|
|
if not re.fullmatch(r'\w+', form.key, re.ASCII):
|
|
return json_response(error='标识符必须为字母、数字和下划线的组合')
|
|
|
|
service = Service.objects.filter(key=form.key).first()
|
|
if service and service.id != form.id:
|
|
return json_response(error='该标识符已存在,请更改后重试')
|
|
app = App.objects.filter(key=form.key).first()
|
|
if app:
|
|
return json_response(error=f'该标识符已被应用 {app.name} 使用,请更改后重试')
|
|
if form.id:
|
|
Service.objects.filter(pk=form.id).update(**form)
|
|
else:
|
|
Service.objects.create(created_by=request.user, **form)
|
|
return json_response(error=error)
|
|
|
|
@auth('config.src.del')
|
|
def delete(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='请指定操作对象')
|
|
).parse(request.GET)
|
|
if error is None:
|
|
rel_apps = []
|
|
for app in App.objects.filter(rel_services__isnull=False):
|
|
rel_services = json.loads(app.rel_services)
|
|
if form.id in rel_services:
|
|
rel_apps.append(app.name)
|
|
if rel_apps:
|
|
return json_response(
|
|
error=f'该服务在配置中心已被 "{", ".join(rel_apps)}" 依赖,请解除依赖关系后再尝试删除。')
|
|
# auto delete configs
|
|
Config.objects.filter(type='src', o_id=form.id).delete()
|
|
ConfigHistory.objects.filter(type='src', o_id=form.id).delete()
|
|
Service.objects.filter(pk=form.id).delete()
|
|
return json_response(error=error)
|
|
|
|
|
|
class ConfigView(View):
|
|
@auth('config.src.view_config|config.app.view_config')
|
|
def get(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='未指定操作对象'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
|
Argument('env_id', type=int, help='缺少必要参数'),
|
|
).parse(request.GET)
|
|
if error is None:
|
|
form.o_id, data = form.pop('id'), []
|
|
for item in Config.objects.filter(**form).annotate(update_user=F('updated_by__nickname')):
|
|
tmp = item.to_dict()
|
|
tmp['update_user'] = item.update_user
|
|
data.append(tmp)
|
|
return json_response(data)
|
|
return json_response(error=error)
|
|
|
|
@auth('config.src.edit_config|config.app.edit_config')
|
|
def post(self, request):
|
|
form, error = JsonParser(
|
|
Argument('o_id', type=int, help='缺少必要参数'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
|
Argument('envs', type=list, filter=lambda x: len(x), help='请选择环境'),
|
|
Argument('key', help='请输入Key'),
|
|
Argument('value', type=str, default=''),
|
|
Argument('desc', required=False)
|
|
).parse(request.body)
|
|
if error is None:
|
|
form.value = form.value.strip()
|
|
form.updated_at = human_datetime()
|
|
form.updated_by = request.user
|
|
envs = form.pop('envs')
|
|
for env_id in envs:
|
|
cf = Config.objects.filter(o_id=form.o_id, type=form.type, env_id=env_id, key=form.key).first()
|
|
if cf:
|
|
raise Exception(f'{cf.env.name} 中已存在该Key')
|
|
Config.objects.create(env_id=env_id, **form)
|
|
ConfigHistory.objects.create(action='1', env_id=env_id, **form)
|
|
return json_response(error=error)
|
|
|
|
@auth('config.src.edit_config|config.app.edit_config')
|
|
def patch(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='缺少必要参数'),
|
|
Argument('value', type=str, default=''),
|
|
Argument('desc', required=False)
|
|
).parse(request.body)
|
|
if error is None:
|
|
form.value = form.value.strip()
|
|
config = Config.objects.filter(pk=form.id).first()
|
|
if not config:
|
|
return json_response(error='未找到指定对象')
|
|
config.desc = form.desc
|
|
if config.value != form.value:
|
|
old_value = config.value
|
|
config.value = form.value
|
|
config.updated_at = human_datetime()
|
|
config.updated_by = request.user
|
|
ConfigHistory.objects.create(
|
|
action='2',
|
|
old_value=old_value,
|
|
**config.to_dict(excludes=('id',)))
|
|
config.save()
|
|
return json_response(error=error)
|
|
|
|
@auth('config.src.edit_config|config.app.edit_config')
|
|
def delete(self, request):
|
|
form, error = JsonParser(
|
|
Argument('id', type=int, help='未指定操作对象')
|
|
).parse(request.GET)
|
|
if error is None:
|
|
config = Config.objects.filter(pk=form.id).first()
|
|
if config:
|
|
ConfigHistory.objects.create(
|
|
action='3',
|
|
old_value=config.value,
|
|
value='',
|
|
updated_at=human_datetime(),
|
|
updated_by=request.user,
|
|
**config.to_dict(excludes=('id', 'value', 'updated_at', 'updated_by_id'))
|
|
)
|
|
config.delete()
|
|
return json_response(error=error)
|
|
|
|
|
|
class HistoryView(View):
|
|
@auth('config.src.view_config|config.app.view_config')
|
|
def post(self, request):
|
|
form, error = JsonParser(
|
|
Argument('o_id', type=int, help='缺少必要参数'),
|
|
Argument('env_id', type=int, help='缺少必要参数'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数')
|
|
).parse(request.body)
|
|
if error is None:
|
|
data = []
|
|
for item in ConfigHistory.objects.filter(**form).annotate(update_user=F('updated_by__nickname')):
|
|
tmp = item.to_dict()
|
|
tmp['action_alias'] = item.get_action_display()
|
|
tmp['update_user'] = item.update_user
|
|
data.append(tmp)
|
|
return json_response(data)
|
|
return json_response(error=error)
|
|
|
|
|
|
@auth('config.src.view_config|config.app.view_config')
|
|
def post_diff(request):
|
|
form, error = JsonParser(
|
|
Argument('o_id', type=int, help='缺少必要参数'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
|
Argument('envs', type=list, filter=lambda x: len(x), help='缺少必要参数'),
|
|
).parse(request.body)
|
|
if error is None:
|
|
data, form.env_id__in = {}, form.pop('envs')
|
|
for item in Config.objects.filter(**form).order_by('key'):
|
|
if item.key in data:
|
|
data[item.key][item.env_id] = item.value
|
|
else:
|
|
data[item.key] = {'key': item.key, item.env_id: item.value}
|
|
return json_response(list(data.values()))
|
|
return json_response(error=error)
|
|
|
|
|
|
@auth('config.src.edit_config|config.app.edit_config')
|
|
def parse_json(request):
|
|
form, error = JsonParser(
|
|
Argument('o_id', type=int, help='缺少必要参数'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
|
Argument('env_id', type=int, help='缺少必要参数'),
|
|
Argument('data', type=dict, help='缺少必要参数')
|
|
).parse(request.body)
|
|
if error is None:
|
|
data = form.pop('data')
|
|
_parse(request, form, data)
|
|
return json_response(error=error)
|
|
|
|
|
|
@auth('config.src.edit_config|config.app.edit_config')
|
|
def parse_text(request):
|
|
form, error = JsonParser(
|
|
Argument('o_id', type=int, help='缺少必要参数'),
|
|
Argument('type', filter=lambda x: x in dict(Config.TYPES), help='缺少必要参数'),
|
|
Argument('env_id', type=int, help='缺少必要参数'),
|
|
Argument('data', handler=str.strip, help='缺少必要参数')
|
|
).parse(request.body)
|
|
if error is None:
|
|
data = {}
|
|
for line in form.pop('data').split('\n'):
|
|
line = line.strip()
|
|
if not line or line[0] in ('#', ';'):
|
|
continue
|
|
fields = line.split('=', 1)
|
|
if len(fields) != 2 or fields[0].strip() == '':
|
|
return json_response(error=f'解析配置{line!r}失败,确认其遵循 key = value 格式')
|
|
data[fields[0].strip()] = fields[1].strip()
|
|
_parse(request, form, data)
|
|
return json_response(error=error)
|
|
|
|
|
|
def _parse(request, query, data):
|
|
for item in Config.objects.filter(**query):
|
|
if item.key in data:
|
|
value = _filter_value(data.pop(item.key))
|
|
if item.value != value:
|
|
old_value = item.value
|
|
item.value = value
|
|
item.updated_at = human_datetime()
|
|
item.updated_by = request.user
|
|
item.save()
|
|
ConfigHistory.objects.create(
|
|
action='2',
|
|
old_value=old_value,
|
|
**item.to_dict(excludes=('id',)))
|
|
else:
|
|
ConfigHistory.objects.create(
|
|
action='3',
|
|
old_value=item.value,
|
|
value='',
|
|
updated_at=human_datetime(),
|
|
updated_by=request.user,
|
|
**item.to_dict(excludes=('id', 'value', 'updated_at', 'updated_by_id'))
|
|
)
|
|
item.delete()
|
|
for key, value in data.items():
|
|
query.key = key
|
|
query.value = _filter_value(value)
|
|
query.updated_at = human_datetime()
|
|
query.updated_by = request.user
|
|
Config.objects.create(**query)
|
|
ConfigHistory.objects.create(action='1', **query)
|
|
|
|
|
|
def _filter_value(value):
|
|
if isinstance(value, (str, int)):
|
|
value = str(value).strip()
|
|
else:
|
|
value = json.dumps(value)
|
|
return value
|