diff --git a/src/snek/mapper/__init__.py b/src/snek/mapper/__init__.py
index 5428f10..69901ec 100644
--- a/src/snek/mapper/__init__.py
+++ b/src/snek/mapper/__init__.py
@@ -10,10 +10,12 @@ from snek.mapper.user import UserMapper
from snek.mapper.user_property import UserPropertyMapper
from snek.mapper.repository import RepositoryMapper
from snek.mapper.channel_attachment import ChannelAttachmentMapper
+from snek.mapper.container import ContainerMapper
from snek.system.object import Object
@functools.cache
+
def get_mappers(app=None):
return Object(
**{
@@ -27,6 +29,7 @@ def get_mappers(app=None):
"user_property": UserPropertyMapper(app=app),
"repository": RepositoryMapper(app=app),
"channel_attachment": ChannelAttachmentMapper(app=app),
+ "container": ContainerMapper(app=app),
}
)
diff --git a/src/snek/mapper/container.py b/src/snek/mapper/container.py
new file mode 100644
index 0000000..f88f9f0
--- /dev/null
+++ b/src/snek/mapper/container.py
@@ -0,0 +1,6 @@
+from snek.model.container import Container
+from snek.system.mapper import BaseMapper
+
+class ContainerMapper(BaseMapper):
+ model_class = Container
+ table_name = "container"
\ No newline at end of file
diff --git a/src/snek/model/__init__.py b/src/snek/model/__init__.py
index 17832c6..dec05e8 100644
--- a/src/snek/model/__init__.py
+++ b/src/snek/model/__init__.py
@@ -12,6 +12,7 @@ from snek.model.user import UserModel
from snek.model.user_property import UserPropertyModel
from snek.model.repository import RepositoryModel
from snek.model.channel_attachment import ChannelAttachmentModel
+from snek.model.container import Container
from snek.system.object import Object
@@ -29,6 +30,7 @@ def get_models():
"user_property": UserPropertyModel,
"repository": RepositoryModel,
"channel_attachment": ChannelAttachmentModel,
+ "container": Container,
}
)
diff --git a/src/snek/model/container.py b/src/snek/model/container.py
new file mode 100644
index 0000000..3947a8d
--- /dev/null
+++ b/src/snek/model/container.py
@@ -0,0 +1,10 @@
+from snek.system.model import BaseModel, ModelField
+
+class Container(BaseModel):
+ id = ModelField(name="id", required=True, kind=str)
+ name = ModelField(name="name", required=True, kind=str)
+ status = ModelField(name="status", required=True, kind=str)
+ resources = ModelField(name="resources", required=False, kind=str)
+ user_uid = ModelField(name="user_uid", required=False, kind=str)
+ path = ModelField(name="path", required=False, kind=str)
+ readonly = ModelField(name="readonly", required=False, kind=bool, default=False)
diff --git a/src/snek/service/__init__.py b/src/snek/service/__init__.py
index dae9e09..5669bdb 100644
--- a/src/snek/service/__init__.py
+++ b/src/snek/service/__init__.py
@@ -13,6 +13,7 @@ from snek.service.user_property import UserPropertyService
from snek.service.util import UtilService
from snek.service.repository import RepositoryService
from snek.service.channel_attachment import ChannelAttachmentService
+from snek.service.container import ContainerService
from snek.system.object import Object
from snek.service.db import DBService
@@ -34,6 +35,7 @@ def get_services(app):
"repository": RepositoryService(app=app),
"db": DBService(app=app),
"channel_attachment": ChannelAttachmentService(app=app),
+ "container": ContainerService(app=app),
}
)
diff --git a/src/snek/service/container.py b/src/snek/service/container.py
new file mode 100644
index 0000000..4025e9e
--- /dev/null
+++ b/src/snek/service/container.py
@@ -0,0 +1,29 @@
+from snek.system.service import BaseService
+
+class ContainerService(BaseService):
+ mapper_name = "container"
+
+ async def create(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False):
+ model = await self.new()
+ model["id"] = id
+ model["name"] = name
+ model["status"] = status
+ if resources:
+ model["resources"] = resources
+ if user_uid:
+ model["user_uid"] = user_uid
+ if path:
+ model["path"] = path
+ model["readonly"] = readonly
+ if await super().save(model):
+ return model
+ raise Exception(f"Failed to create container: {model.errors}")
+
+ async def get(self, id):
+ return await self.mapper.get(id)
+
+ async def update(self, model):
+ return await self.mapper.update(model)
+
+ async def delete(self, id):
+ return await self.mapper.delete(id)
diff --git a/src/snek/templates/settings/containers/create.html b/src/snek/templates/settings/containers/create.html
new file mode 100644
index 0000000..9b60f3e
--- /dev/null
+++ b/src/snek/templates/settings/containers/create.html
@@ -0,0 +1,39 @@
+{% extends 'settings/index.html' %}
+
+{% block header_text %}
Create Container
{% endblock %}
+
+{% block main %}
+{% include 'settings/containers/form.html' %}
+
+{% endblock %}
diff --git a/src/snek/templates/settings/containers/delete.html b/src/snek/templates/settings/containers/delete.html
new file mode 100644
index 0000000..a0fc249
--- /dev/null
+++ b/src/snek/templates/settings/containers/delete.html
@@ -0,0 +1,17 @@
+{% extends 'settings/index.html' %}
+
+{% block header_text %} Delete Container
{% endblock %}
+
+{% block main %}
+
+
Are you sure you want to delete the following container?
+
{{ container.name }}
+
+
+{% endblock %}
diff --git a/src/snek/templates/settings/containers/form.html b/src/snek/templates/settings/containers/form.html
new file mode 100644
index 0000000..8785cc6
--- /dev/null
+++ b/src/snek/templates/settings/containers/form.html
@@ -0,0 +1,28 @@
+
+
+
+
diff --git a/src/snek/templates/settings/containers/index.html b/src/snek/templates/settings/containers/index.html
new file mode 100644
index 0000000..a6855dd
--- /dev/null
+++ b/src/snek/templates/settings/containers/index.html
@@ -0,0 +1,96 @@
+{% extends 'settings/index.html' %}
+
+{% block header_text %} Containers
{% endblock %}
+
+{% block main %}
+
+
+
+
+ Containers - List
+
+
+
+
+
+
+
+ {% for container in containers %}
+
+
+ {{ container.name }}
+ {{ container.status }}
+
+
+ {% if container.readonly %}Readonly{% else %}Writable{% endif %}
+
+
+
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/src/snek/templates/settings/containers/update.html b/src/snek/templates/settings/containers/update.html
new file mode 100644
index 0000000..1c258cd
--- /dev/null
+++ b/src/snek/templates/settings/containers/update.html
@@ -0,0 +1,40 @@
+{% extends "settings/index.html" %}
+
+{% block header_text %} Update Container
{% endblock %}
+
+{% block main %}
+{% include "settings/containers/form.html" %}
+
+{% endblock %}
diff --git a/src/snek/view/settings/containers.py b/src/snek/view/settings/containers.py
new file mode 100644
index 0000000..dae0237
--- /dev/null
+++ b/src/snek/view/settings/containers.py
@@ -0,0 +1,91 @@
+import asyncio
+from aiohttp import web
+
+from snek.system.view import BaseFormView
+import pathlib
+
+class ContainersIndexView(BaseFormView):
+
+ login_required = True
+
+ async def get(self):
+
+ user_uid = self.session.get("uid")
+
+ containers = []
+ async for container in self.services.container.find(user_uid=user_uid):
+ containers.append(container.record)
+
+ user = await self.services.user.get(uid=self.session.get("uid"))
+
+ return await self.render_template("settings/containers/index.html", {"containers": containers, "user": user})
+
+class ContainersCreateView(BaseFormView):
+
+ login_required = True
+
+ async def get(self):
+
+ return await self.render_template("settings/containers/create.html")
+
+ async def post(self):
+ data = await self.request.post()
+ container = await self.services.container.create(
+ user_uid=self.session.get("uid"),
+ name=data['name'],
+ status=data['status'],
+ resources=data.get('resources', ''),
+ path=data.get('path', ''),
+ readonly=bool(data.get('readonly', False))
+ )
+ return web.HTTPFound("/settings/containers/index.html")
+
+class ContainersUpdateView(BaseFormView):
+
+ login_required = True
+
+ async def get(self):
+
+ container = await self.services.container.get(
+ user_uid=self.session.get("uid"), uid=self.request.match_info["uid"]
+ )
+ if not container:
+ return web.HTTPNotFound()
+ return await self.render_template("settings/containers/update.html", {"container": container.record})
+
+ async def post(self):
+ data = await self.request.post()
+ container = await self.services.container.get(
+ user_uid=self.session.get("uid"), uid=self.request.match_info["uid"]
+ )
+ container['status'] = data['status']
+ container['resources'] = data.get('resources', '')
+ container['path'] = data.get('path', '')
+ container['readonly'] = bool(data.get('readonly', False))
+ await self.services.container.save(container)
+ return web.HTTPFound("/settings/containers/index.html")
+
+class ContainersDeleteView(BaseFormView):
+
+ login_required = True
+
+ async def get(self):
+
+ container = await self.services.container.get(
+ user_uid=self.session.get("uid"), uid=self.request.match_info["uid"]
+ )
+ if not container:
+ return web.HTTPNotFound()
+
+ return await self.render_template("settings/containers/delete.html", {"container": container.record})
+
+ async def post(self):
+ user_uid = self.session.get("uid")
+ uid = self.request.match_info["uid"]
+ container = await self.services.container.get(
+ user_uid=user_uid, uid=uid
+ )
+ if not container:
+ return web.HTTPNotFound()
+ await self.services.container.delete(user_uid=user_uid, uid=uid)
+ return web.HTTPFound("/settings/containers/index.html")