Update.
This commit is contained in:
parent
c56bf4fb49
commit
ee40c905d4
src/snek
@ -1,11 +1,14 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import uvloop
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
import asyncio
|
||||||
from snek.app import Application
|
from snek.app import Application
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
|
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description="Run the web application.")
|
parser = argparse.ArgumentParser(description="Run the web application.")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--port",
|
"--port",
|
||||||
|
@ -39,6 +39,10 @@ from snek.view.logout import LogoutView
|
|||||||
from snek.view.register import RegisterView
|
from snek.view.register import RegisterView
|
||||||
from snek.view.rpc import RPCView
|
from snek.view.rpc import RPCView
|
||||||
from snek.view.search_user import SearchUserView
|
from snek.view.search_user import SearchUserView
|
||||||
|
from snek.view.settings.repositories import RepositoriesIndexView
|
||||||
|
from snek.view.settings.repositories import RepositoriesCreateView
|
||||||
|
from snek.view.settings.repositories import RepositoriesUpdateView
|
||||||
|
from snek.view.settings.repositories import RepositoriesDeleteView
|
||||||
from snek.view.settings.index import SettingsIndexView
|
from snek.view.settings.index import SettingsIndexView
|
||||||
from snek.view.settings.profile import SettingsProfileView
|
from snek.view.settings.profile import SettingsProfileView
|
||||||
from snek.view.stats import StatsView
|
from snek.view.stats import StatsView
|
||||||
@ -175,6 +179,10 @@ class Application(BaseApplication):
|
|||||||
self.router.add_view("/drive/{drive}.json", DriveView)
|
self.router.add_view("/drive/{drive}.json", DriveView)
|
||||||
self.router.add_view("/stats.json", StatsView)
|
self.router.add_view("/stats.json", StatsView)
|
||||||
self.router.add_view("/user/{user}.html", UserView)
|
self.router.add_view("/user/{user}.html", UserView)
|
||||||
|
self.router.add_view("/settings/repositories/index.html", RepositoriesIndexView)
|
||||||
|
self.router.add_view("/settings/repositories/create.html", RepositoriesCreateView)
|
||||||
|
self.router.add_view("/settings/repositories/repository/{name}/update.html", RepositoriesUpdateView)
|
||||||
|
self.router.add_view("/settings/repositories/respository/{name}/delete.html", RepositoriesDeleteView)
|
||||||
self.webdav = WebdavApplication(self)
|
self.webdav = WebdavApplication(self)
|
||||||
self.add_subapp("/webdav", self.webdav)
|
self.add_subapp("/webdav", self.webdav)
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from snek.mapper.drive_item import DriveItemMapper
|
|||||||
from snek.mapper.notification import NotificationMapper
|
from snek.mapper.notification import NotificationMapper
|
||||||
from snek.mapper.user import UserMapper
|
from snek.mapper.user import UserMapper
|
||||||
from snek.mapper.user_property import UserPropertyMapper
|
from snek.mapper.user_property import UserPropertyMapper
|
||||||
|
from snek.mapper.repository import RepositoryMapper
|
||||||
from snek.system.object import Object
|
from snek.system.object import Object
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ def get_mappers(app=None):
|
|||||||
"drive_item": DriveItemMapper(app=app),
|
"drive_item": DriveItemMapper(app=app),
|
||||||
"drive": DriveMapper(app=app),
|
"drive": DriveMapper(app=app),
|
||||||
"user_property": UserPropertyMapper(app=app),
|
"user_property": UserPropertyMapper(app=app),
|
||||||
|
"repository": RepositoryMapper(app=app),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
7
src/snek/mapper/repository.py
Normal file
7
src/snek/mapper/repository.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from snek.model.repository import RepositoryModel
|
||||||
|
from snek.system.mapper import BaseMapper
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryMapper(BaseMapper):
|
||||||
|
model_class = RepositoryModel
|
||||||
|
table_name = "repository"
|
@ -10,6 +10,7 @@ from snek.model.drive_item import DriveItemModel
|
|||||||
from snek.model.notification import NotificationModel
|
from snek.model.notification import NotificationModel
|
||||||
from snek.model.user import UserModel
|
from snek.model.user import UserModel
|
||||||
from snek.model.user_property import UserPropertyModel
|
from snek.model.user_property import UserPropertyModel
|
||||||
|
from snek.model.repository import RepositoryModel
|
||||||
from snek.system.object import Object
|
from snek.system.object import Object
|
||||||
|
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ def get_models():
|
|||||||
"drive": DriveModel,
|
"drive": DriveModel,
|
||||||
"notification": NotificationModel,
|
"notification": NotificationModel,
|
||||||
"user_property": UserPropertyModel,
|
"user_property": UserPropertyModel,
|
||||||
|
"repository": RepositoryModel,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
14
src/snek/model/repository.py
Normal file
14
src/snek/model/repository.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from snek.model.user import UserModel
|
||||||
|
from snek.system.model import BaseModel, ModelField
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryModel(BaseModel):
|
||||||
|
|
||||||
|
user_uid = ModelField(name="user_uid", required=True, kind=str)
|
||||||
|
|
||||||
|
name = ModelField(name="name", required=True, kind=str)
|
||||||
|
|
||||||
|
is_private = ModelField(name="is_private", required=False, kind=bool)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -11,6 +11,7 @@ from snek.service.socket import SocketService
|
|||||||
from snek.service.user import UserService
|
from snek.service.user import UserService
|
||||||
from snek.service.user_property import UserPropertyService
|
from snek.service.user_property import UserPropertyService
|
||||||
from snek.service.util import UtilService
|
from snek.service.util import UtilService
|
||||||
|
from snek.service.repository import RepositoryService
|
||||||
from snek.system.object import Object
|
from snek.system.object import Object
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ def get_services(app):
|
|||||||
"drive": DriveService(app=app),
|
"drive": DriveService(app=app),
|
||||||
"drive_item": DriveItemService(app=app),
|
"drive_item": DriveItemService(app=app),
|
||||||
"user_property": UserPropertyService(app=app),
|
"user_property": UserPropertyService(app=app),
|
||||||
|
"repository": RepositoryService(app=app),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
39
src/snek/service/repository.py
Normal file
39
src/snek/service/repository.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from snek.system.service import BaseService
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
class RepositoryService(BaseService):
|
||||||
|
mapper_name = "repository"
|
||||||
|
|
||||||
|
async def exists(self, user_uid, name, **kwargs):
|
||||||
|
kwargs["user_uid"] = user_uid
|
||||||
|
kwargs["name"] = name
|
||||||
|
return await self.exists(**kwargs)
|
||||||
|
|
||||||
|
async def init(self, user_uid, name):
|
||||||
|
repository_path = await self.services.user.get_repository_path(user_uid)
|
||||||
|
if not repository_path.exists():
|
||||||
|
repository_path.mkdir(parents=True)
|
||||||
|
repository_path = repository_path.joinpath(name)
|
||||||
|
command = ['git', 'init', '--bare', repository_path]
|
||||||
|
process = await asyncio.subprocess.create_subprocess_exec(
|
||||||
|
*command,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = await process.communicate()
|
||||||
|
if process.returncode == 0:
|
||||||
|
print(f"Bare Git repository created at: {repo_path}")
|
||||||
|
else:
|
||||||
|
print(f"Error creating repository: {stderr.decode().strip()}")
|
||||||
|
|
||||||
|
async def create(self, user_uid, name,is_private=False):
|
||||||
|
if await self.exists(user_uid=user_uid, name=name):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
model = await self.new()
|
||||||
|
model["user_uid"] = user_uid
|
||||||
|
model["name"] = name
|
||||||
|
model["is_private"] = is_private
|
||||||
|
return await self.save(model)
|
@ -42,6 +42,12 @@ class UserService(BaseService):
|
|||||||
def get_admin_uids(self):
|
def get_admin_uids(self):
|
||||||
return self.mapper.get_admin_uids()
|
return self.mapper.get_admin_uids()
|
||||||
|
|
||||||
|
async def get_repository_path(self, user_uid):
|
||||||
|
path = pathlib.Path(f"./drive/repositories/{user_uid}")
|
||||||
|
if not path.exists():
|
||||||
|
return None
|
||||||
|
return path
|
||||||
|
|
||||||
async def get_static_path(self, user_uid):
|
async def get_static_path(self, user_uid):
|
||||||
path = pathlib.Path(f"./drive/{user_uid}/snek/static")
|
path = pathlib.Path(f"./drive/{user_uid}/snek/static")
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
|
59
src/snek/templates/settings/repositories/create.html
Normal file
59
src/snek/templates/settings/repositories/create.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{% extends 'settings/index.html' %}
|
||||||
|
|
||||||
|
{% block header_text %}<h1><i class="fa-solid fa-plus"></i> Create Repository</h1>{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
div,input,label,button{
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
|
||||||
|
input[type="text"] {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #ccc; border-radius: 5px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button, a.button {
|
||||||
|
background: #198754; color: #fff; border: none; border-radius: 5px;
|
||||||
|
padding: 0.1rem 0.8rem; text-decoration: none; cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
|
||||||
|
}
|
||||||
|
.
|
||||||
|
.cancel {
|
||||||
|
background: #6c757d;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container { max-width: 98vw; }
|
||||||
|
form { padding: 1rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<form action="/settings/repositories/create.html" method="post">
|
||||||
|
<div>
|
||||||
|
<label for="name"><i class="fa-solid fa-book"></i> Name</label>
|
||||||
|
<input type="text" id="name" name="name" required placeholder="Repository name">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="is_private" value="1">
|
||||||
|
<i class="fa-solid fa-lock"></i> Private
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit"><i class="fa-solid fa-plus"></i> Create</button>
|
||||||
|
<button onclick="history.back()" class="cancel"><i class="fa-solid fa-arrow-left"></i> Back</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
63
src/snek/templates/settings/repositories/delete.html
Normal file
63
src/snek/templates/settings/repositories/delete.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Delete Repository</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; margin: 2rem; }
|
||||||
|
.container { max-width: 400px; margin: 0 auto; }
|
||||||
|
.confirm-box {
|
||||||
|
background: #ffe5e8;
|
||||||
|
border: 1.5px solid #dc3545;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.repo-name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
display: flex; gap: 1rem; justify-content: center; margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
button, a {
|
||||||
|
background: #dc3545; color: #fff;
|
||||||
|
border: none; border-radius: 5px; padding: 0.6rem 1.2rem;
|
||||||
|
font-size: 1rem; cursor: pointer;
|
||||||
|
display: flex; align-items: center; gap: 0.5rem; text-decoration: none; justify-content: center;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
.cancel {
|
||||||
|
background: #6c757d;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container { max-width: 98vw; }
|
||||||
|
.confirm-box { padding: 1rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1><i class="fa-solid fa-trash-can"></i> Delete Repository</h1>
|
||||||
|
<div class="confirm-box">
|
||||||
|
<div>
|
||||||
|
<i class="fa-solid fa-triangle-exclamation" style="font-size:2rem; color:#dc3545;"></i>
|
||||||
|
</div>
|
||||||
|
<p>Are you sure you want to <strong>delete</strong> the following repository?</p>
|
||||||
|
<div class="repo-name"><i class="fa-solid fa-book"></i> my-first-repo</div>
|
||||||
|
<form action="/repositories/delete" method="post" style="margin-top:1.5rem;">
|
||||||
|
<input type="hidden" name="id" value="1">
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit"><i class="fa-solid fa-trash"></i> Yes, delete</button>
|
||||||
|
<a href="repositories.html" class="cancel"><i class="fa-solid fa-ban"></i> Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
106
src/snek/templates/settings/repositories/index.html
Normal file
106
src/snek/templates/settings/repositories/index.html
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
{% extends 'settings/index.html' %}
|
||||||
|
|
||||||
|
{% block header_text %}<h1><i class="fa-solid fa-database"></i> Repositories</h1>{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Repositories - List</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.repo-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.repo-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.repo-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
.repo-name {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.repo-row { flex-direction: column; align-items: stretch; }
|
||||||
|
.actions { justify-content: flex-start; }
|
||||||
|
}
|
||||||
|
.topbar {
|
||||||
|
display: flex;
|
||||||
|
/* justify-content: flex-end;*/
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
button, a.button {
|
||||||
|
background: #198754; color: #fff; border: none; border-radius: 5px;
|
||||||
|
padding: 0.4rem 0.8rem; text-decoration: none; cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
|
||||||
|
}
|
||||||
|
.button.delete { background: #dc3545; }
|
||||||
|
.button.edit { background: #0d6efd; }
|
||||||
|
.button.clone { background: #6c757d; }
|
||||||
|
.button.browse { background: #ffc107; color: #212529; }
|
||||||
|
.button.create { background: #20c997; margin-left: 0.5rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="topbar">
|
||||||
|
<a class="button create" href="/settings/repositories/create.html">
|
||||||
|
<i class="fa-solid fa-plus"></i> New Repository
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<section class="repo-list">
|
||||||
|
<!-- Example repository entries; replace with your templating/iteration -->
|
||||||
|
{% for repo in repositories %}
|
||||||
|
<div class="repo-row">
|
||||||
|
<div class="repo-info">
|
||||||
|
<span class="repo-name"><i class="fa-solid fa-book"></i> {{ repo.name }}</span>
|
||||||
|
|
||||||
|
<span title="Public">
|
||||||
|
<i class="fa-solid {% if repo.is_private %}fa-lock{% else %}fa-lock-open{% endif %}"></i>
|
||||||
|
{% if repo.is_private %}Private{% else %}Public{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<a class="button browse" href="/repositories/{{ user.username }}/{{ repo.name }}" target="_blank">
|
||||||
|
<i class="fa-solid fa-folder-open"></i> Browse
|
||||||
|
</a>
|
||||||
|
<a class="button clone" href="/repositories/{{ user.username }}/{{ repo.name }}/clone">
|
||||||
|
<i class="fa-solid fa-code-branch"></i> Clone
|
||||||
|
</a>
|
||||||
|
<a class="button edit" href="/settings/repositories/repository/{{ repo.name }}/update.html">
|
||||||
|
<i class="fa-solid fa-pen"></i> Edit
|
||||||
|
</a>
|
||||||
|
<a class="button delete" href="/settings/repositories/{{ repo.name }}/delete.html">
|
||||||
|
<i class="fa-solid fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<!-- ... -->
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{% endblock %}
|
45
src/snek/templates/settings/repositories/update.html
Normal file
45
src/snek/templates/settings/repositories/update.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{% extends "settings/index.html" %}
|
||||||
|
|
||||||
|
{% block header_text %}<h1><i class="fa-solid fa-pen"></i> Update Repository</h1>{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
form {
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
|
||||||
|
button {
|
||||||
|
background: #0d6efd; color: #fff;
|
||||||
|
border: none; border-radius: 5px; padding: 0.6rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
|
||||||
|
}
|
||||||
|
.cancel {
|
||||||
|
background: #6c757d;
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container { max-width: 98vw; }
|
||||||
|
form { padding: 1rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="container">
|
||||||
|
<form method="post">
|
||||||
|
<!-- Assume hidden id for backend use -->
|
||||||
|
<input type="hidden" name="id" value="{{ repository.id }}">
|
||||||
|
<div>
|
||||||
|
<label for="name"><i class="fa-solid fa-book"></i> Name</label>
|
||||||
|
<input type="text" id="name" name="name" value="{{ repository.name }}" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="is_private" value="1" {% if repository.is_private %}checked{% endif %}>
|
||||||
|
<i class="fa-solid fa-lock"></i> Private
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit"><i class="fa-solid fa-pen"></i> Update</button>
|
||||||
|
<button onclick="history.back()" class="cancel"><i class="fa-solid fa-arrow-left"></i>Cancel</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -3,7 +3,7 @@
|
|||||||
<h2>You</h2>
|
<h2>You</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="no-select" href="/settings/profile.html">Profile</a></li>
|
<li><a class="no-select" href="/settings/profile.html">Profile</a></li>
|
||||||
<li><a class="no-select" href="/settings/gists.html">Gists</a></li>
|
<li><a class="no-select" href="/settings/repositories/index.html">Repositories</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
|
68
src/snek/view/settings/repositories.py
Normal file
68
src/snek/view/settings/repositories.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import asyncio
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
from snek.system.view import BaseFormView
|
||||||
|
import pathlib
|
||||||
|
|
||||||
|
class RepositoriesIndexView(BaseFormView):
|
||||||
|
|
||||||
|
login_required = True
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
|
||||||
|
user_uid = self.session.get("uid")
|
||||||
|
|
||||||
|
repositories = []
|
||||||
|
async for repository in self.services.repository.find(user_uid=user_uid):
|
||||||
|
repositories.append(repository.record)
|
||||||
|
|
||||||
|
return await self.render_template("settings/repositories/index.html", {"repositories": repositories})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoriesCreateView(BaseFormView):
|
||||||
|
|
||||||
|
login_required = True
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
|
||||||
|
return await self.render_template("settings/repositories/create.html")
|
||||||
|
|
||||||
|
async def post(self):
|
||||||
|
data = await self.request.post()
|
||||||
|
repository = await self.services.repository.create(user_uid=self.session.get("uid"), name=data['name'], is_private=int(data.get('is_private',0)))
|
||||||
|
return web.HTTPFound("/settings/repositories/index.html")
|
||||||
|
|
||||||
|
class RepositoriesUpdateView(BaseFormView):
|
||||||
|
|
||||||
|
login_required = True
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
|
||||||
|
repository = await self.services.repository.get(
|
||||||
|
user_uid=self.session.get("uid"), name=self.request.match_info["name"]
|
||||||
|
)
|
||||||
|
if not repository:
|
||||||
|
return web.HTTPNotFound()
|
||||||
|
return await self.render_template("settings/repositories/update.html", {"repository": repository.record})
|
||||||
|
|
||||||
|
async def post(self):
|
||||||
|
data = await self.request.post()
|
||||||
|
repository = await self.services.repository.get(
|
||||||
|
user_uid=self.session.get("uid"), name=self.request.match_info["name"]
|
||||||
|
)
|
||||||
|
repository['is_private'] = int(data.get('is_private',0))
|
||||||
|
await self.services.repository.save(repository)
|
||||||
|
return web.HTTPFound("/settings/repositories/index.html")
|
||||||
|
|
||||||
|
class RepositoriesDeleteView(BaseFormView):
|
||||||
|
|
||||||
|
login_required = True
|
||||||
|
|
||||||
|
async def get(self):
|
||||||
|
|
||||||
|
return await self.render_template("settings/repositories/delete.html")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user