This commit is contained in:
retoor 2025-06-07 05:46:30 +02:00
parent 8e9ee4bff0
commit f182c2209e
4 changed files with 128 additions and 2 deletions
src/snek

View File

@ -8,6 +8,15 @@ import pathlib
class ChannelService(BaseService):
mapper_name = "channel"
async def get_home_folder(self, channel_uid):
folder = pathlib.Path(f"./drive/{channel_uid}/container/home")
if not folder.exists():
try:
folder.mkdir(parents=True, exist_ok=True)
except:
pass
return folder
async def get_attachment_folder(self, channel_uid,ensure=False):
path = pathlib.Path(f"./drive/{channel_uid}/attachments")
if ensure:
@ -56,6 +65,7 @@ class ChannelService(BaseService):
model["is_private"] = is_private
model["is_listed"] = is_listed
if await self.save(model):
await self.services.container.create(model["uid"])
return model
raise Exception(f"Failed to create channel: {model.errors}.")

View File

@ -1,9 +1,36 @@
from snek.system.service import BaseService
from snek.system.docker import ComposeFileManager
class ContainerService(BaseService):
mapper_name = "container"
async def create(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.compose_path = "snek-container-compose.yml"
self.compose = ComposeFileManager(self.compose_path)
async def get_instances(self):
return list(self.compose.list_instances())
async def get_container_name(self, channel_uid):
return f"channel-{channel_uid}"
async def create(self, channel_uid, image="ubuntu:latest", command=None, cpus=1, memory='1024m', ports=None, volumes=None):
name = await self.get_container_name(channel_uid)
self.compose.create_instance(
name,
image,
command,
cpus,
memory,
ports,
["./"+str((await self.services.channel.get_home_folder(channel_uid))) +":"+ "/home/ubuntu"]
)
async def create2(self, id, name, status, resources=None, user_uid=None, path=None, readonly=False):
model = await self.new()
model["id"] = id
model["name"] = name

75
src/snek/system/docker.py Normal file
View File

@ -0,0 +1,75 @@
import yaml
import copy
class ComposeFileManager:
def __init__(self, compose_path='docker-compose.yml'):
self.compose_path = compose_path
self._load()
def _load(self):
try:
with open(self.compose_path, 'r') as f:
self.compose = yaml.safe_load(f) or {}
except FileNotFoundError:
self.compose = {'version': '3', 'services': {}}
def _save(self):
with open(self.compose_path, 'w') as f:
yaml.dump(self.compose, f, default_flow_style=False)
def list_instances(self):
return list(self.compose.get('services', {}).keys())
def create_instance(self, name, image, command=None, cpus=None, memory=None, ports=None, volumes=None):
service = {
'image': image,
}
if command:
service['command'] = command
if cpus or memory:
service['deploy'] = {'resources': {'limits': {}}}
if cpus:
service['deploy']['resources']['limits']['cpus'] = str(cpus)
if memory:
service['deploy']['resources']['limits']['memory'] = str(memory)
if ports:
service['ports'] = [f"{host}:{container}" for container, host in ports.items()]
if volumes:
service['volumes'] = volumes
self.compose.setdefault('services', {})[name] = service
self._save()
def remove_instance(self, name):
if name in self.compose.get('services', {}):
del self.compose['services'][name]
self._save()
def get_instance(self, name):
return self.compose.get('services', {}).get(name)
def duplicate_instance(self, name, new_name):
orig = self.get_instance(name)
if not orig:
raise ValueError(f"No such instance: {name}")
self.compose['services'][new_name] = copy.deepcopy(orig)
self._save()
def update_instance(self, name, **kwargs):
service = self.get_instance(name)
if not service:
raise ValueError(f"No such instance: {name}")
for k, v in kwargs.items():
if v is not None:
service[k] = v
self.compose['services'][name] = service
self._save()
# Storage size is not tracked in compose files; would need Docker API for that.
# Example usage:
# mgr = ComposeFileManager()
# mgr.create_instance('web', 'nginx:latest', cpus=1, memory='512m', ports={80:8080}, volumes=['./data:/data'])
# print(mgr.list_instances())
# mgr.duplicate_instance('web', 'web_copy')
# mgr.remove_instance('web_copy')

View File

@ -5,7 +5,21 @@
{% block main %}
<section class="chat-area">
<message-list class="chat-messages">
<message-list class="chat-messages">
{% if not messages %}
<div>
<h1>Welcome to your new channel!</h1>
<p>This is the start of something great. Use the commands below to get started:</p>
<ul>
<li>Press <code>/invite</code> to invite someone.</li>
<li>Press <code>/online</code> to see who's currently online.</li>
<li>Press <code>/help</code> to view all available commands.</li>
</ul>
<p>Enjoy chatting!</p>
</div>
{% endif %}
{% for message in messages %}
{% autoescape false %}
{{ message.html }}