Compare commits
15 Commits
6337350b60
...
5e99e894e9
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e99e894e9 | |||
| 89d639e44e | |||
| e62da0aef1 | |||
| 3759306e38 | |||
| fcd91b4321 | |||
| cf32a78ef5 | |||
| 7c43d957bc | |||
| cc6a9ef9d3 | |||
| 1babfa0d64 | |||
| ce940b39b8 | |||
| 6151fc1dac | |||
| 338bdb5932 | |||
| bbcc845c26 | |||
| 1c080bc4be | |||
| 59b0494328 |
@ -39,7 +39,9 @@ dependencies = [
|
||||
"Pillow",
|
||||
"pillow-heif",
|
||||
"IP2Location",
|
||||
"bleach"
|
||||
"bleach",
|
||||
"sentry-sdk",
|
||||
"aiosqlite"
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
|
||||
@ -9,6 +9,8 @@ from snek.shell import Shell
|
||||
from snek.app import Application
|
||||
|
||||
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
@ -122,6 +124,12 @@ def shell(db_path):
|
||||
Shell(db_path).run()
|
||||
|
||||
def main():
|
||||
try:
|
||||
import sentry_sdk
|
||||
sentry_sdk.init("https://ab6147c2f3354c819768c7e89455557b@gt.molodetz.nl/1")
|
||||
except ImportError:
|
||||
print("Could not import sentry_sdk")
|
||||
|
||||
cli()
|
||||
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ from contextlib import asynccontextmanager
|
||||
|
||||
from snek import snode
|
||||
from snek.view.threads import ThreadsView
|
||||
|
||||
from snek.system.ads import AsyncDataSet
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from ipaddress import ip_address
|
||||
@ -142,6 +142,7 @@ class Application(BaseApplication):
|
||||
client_max_size=1024 * 1024 * 1024 * 5 * args,
|
||||
**kwargs,
|
||||
)
|
||||
self.db = AsyncDataSet(kwargs["db_path"].replace("sqlite:///", ""))
|
||||
session_setup(self, EncryptedCookieStorage(SESSION_KEY))
|
||||
self.tasks = asyncio.Queue()
|
||||
self._middlewares.append(session_middleware)
|
||||
@ -174,7 +175,7 @@ class Application(BaseApplication):
|
||||
self.on_startup.append(self.prepare_asyncio)
|
||||
self.on_startup.append(self.start_user_availability_service)
|
||||
self.on_startup.append(self.start_ssh_server)
|
||||
self.on_startup.append(self.prepare_database)
|
||||
#self.on_startup.append(self.prepare_database)
|
||||
|
||||
async def prepare_stats(self, app):
|
||||
app['stats'] = create_stats_structure()
|
||||
@ -245,18 +246,8 @@ class Application(BaseApplication):
|
||||
|
||||
|
||||
async def prepare_database(self, app):
|
||||
self.db.query("PRAGMA journal_mode=WAL")
|
||||
self.db.query("PRAGMA syncnorm=off")
|
||||
|
||||
try:
|
||||
if not self.db["user"].has_index("username"):
|
||||
self.db["user"].create_index("username", unique=True)
|
||||
if not self.db["channel_member"].has_index(["channel_uid", "user_uid"]):
|
||||
self.db["channel_member"].create_index(["channel_uid", "user_uid"])
|
||||
if not self.db["channel_message"].has_index(["channel_uid", "user_uid"]):
|
||||
self.db["channel_message"].create_index(["channel_uid", "user_uid"])
|
||||
except:
|
||||
pass
|
||||
await self.db.query_raw("PRAGMA journal_mode=WAL")
|
||||
await self.db.query_raw("PRAGMA syncnorm=off")
|
||||
|
||||
await self.services.drive.prepare_all()
|
||||
self.loop.create_task(self.task_runner())
|
||||
|
||||
@ -77,7 +77,7 @@ class SocketService(BaseService):
|
||||
|
||||
async def send_to_user(self, user_uid, message):
|
||||
count = 0
|
||||
for s in self.users.get(user_uid, []):
|
||||
for s in list(self.users.get(user_uid, [])):
|
||||
if await s.send_json(message):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
@ -101,6 +101,8 @@ class UserService(BaseService):
|
||||
model.username.value = username
|
||||
model.password.value = await security.hash(password)
|
||||
if await self.save(model):
|
||||
for x in range(10):
|
||||
print("Jazeker!!!")
|
||||
if model:
|
||||
channel = await self.services.channel.ensure_public_channel(
|
||||
model["uid"]
|
||||
|
||||
@ -7,7 +7,8 @@ class UserPropertyService(BaseService):
|
||||
mapper_name = "user_property"
|
||||
|
||||
async def set(self, user_uid, name, value):
|
||||
self.mapper.db["user_property"].upsert(
|
||||
self.mapper.db.upsert(
|
||||
"user_property",
|
||||
{
|
||||
"user_uid": user_uid,
|
||||
"name": name,
|
||||
|
||||
@ -1,226 +1,494 @@
|
||||
import { NjetComponent} from "/njet.js"
|
||||
import { NjetComponent } from "/njet.js"
|
||||
|
||||
class NjetEditor extends NjetComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
class NjetEditor extends NjetComponent {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
#editor {
|
||||
padding: 1rem;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.5;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
#command-line {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.2rem 1rem;
|
||||
background: #333;
|
||||
color: #0f0;
|
||||
display: none;
|
||||
font-family: monospace;
|
||||
}
|
||||
`;
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
|
||||
this.editor = document.createElement('div');
|
||||
this.editor.id = 'editor';
|
||||
this.editor.contentEditable = true;
|
||||
this.editor.innerText = `Welcome to VimEditor Component
|
||||
#editor {
|
||||
padding: 1rem;
|
||||
outline: none;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.5;
|
||||
height: calc(100% - 30px);
|
||||
overflow-y: auto;
|
||||
background: #1e1e1e;
|
||||
color: #d4d4d4;
|
||||
font-size: 14px;
|
||||
caret-color: #fff;
|
||||
}
|
||||
|
||||
#editor.insert-mode {
|
||||
caret-color: #4ec9b0;
|
||||
}
|
||||
|
||||
#editor.visual-mode {
|
||||
caret-color: #c586c0;
|
||||
}
|
||||
|
||||
#editor::selection {
|
||||
background: #264f78;
|
||||
}
|
||||
|
||||
#status-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
background: #007acc;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 1rem;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#mode-indicator {
|
||||
text-transform: uppercase;
|
||||
margin-right: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#command-line {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.3rem 1rem;
|
||||
background: #2d2d2d;
|
||||
color: #d4d4d4;
|
||||
display: none;
|
||||
font-family: inherit;
|
||||
border-top: 1px solid #3e3e3e;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#command-input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.visual-selection {
|
||||
background: #264f78 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
this.editor = document.createElement('div');
|
||||
this.editor.id = 'editor';
|
||||
this.editor.contentEditable = true;
|
||||
this.editor.spellcheck = false;
|
||||
this.editor.innerText = `Welcome to VimEditor Component
|
||||
Line 2 here
|
||||
Another line
|
||||
Try i, Esc, v, :, yy, dd, 0, $, gg, G, and p`;
|
||||
|
||||
this.cmdLine = document.createElement('div');
|
||||
this.cmdLine.id = 'command-line';
|
||||
this.shadowRoot.append(style, this.editor, this.cmdLine);
|
||||
this.cmdLine = document.createElement('div');
|
||||
this.cmdLine.id = 'command-line';
|
||||
|
||||
this.mode = 'normal'; // normal | insert | visual | command
|
||||
this.keyBuffer = '';
|
||||
this.lastDeletedLine = '';
|
||||
this.yankedLine = '';
|
||||
const cmdPrompt = document.createElement('span');
|
||||
cmdPrompt.textContent = ':';
|
||||
|
||||
this.editor.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
}
|
||||
this.cmdInput = document.createElement('input');
|
||||
this.cmdInput.id = 'command-input';
|
||||
this.cmdInput.type = 'text';
|
||||
|
||||
connectedCallback() {
|
||||
this.cmdLine.append(cmdPrompt, this.cmdInput);
|
||||
|
||||
this.statusBar = document.createElement('div');
|
||||
this.statusBar.id = 'status-bar';
|
||||
|
||||
this.modeIndicator = document.createElement('span');
|
||||
this.modeIndicator.id = 'mode-indicator';
|
||||
this.modeIndicator.textContent = 'NORMAL';
|
||||
|
||||
this.statusBar.appendChild(this.modeIndicator);
|
||||
|
||||
this.shadowRoot.append(style, this.editor, this.cmdLine, this.statusBar);
|
||||
|
||||
this.mode = 'normal';
|
||||
this.keyBuffer = '';
|
||||
this.lastDeletedLine = '';
|
||||
this.yankedLine = '';
|
||||
this.visualStartOffset = null;
|
||||
this.visualEndOffset = null;
|
||||
|
||||
// Bind event handlers
|
||||
this.handleKeydown = this.handleKeydown.bind(this);
|
||||
this.handleCmdKeydown = this.handleCmdKeydown.bind(this);
|
||||
this.updateVisualSelection = this.updateVisualSelection.bind(this);
|
||||
|
||||
this.editor.addEventListener('keydown', this.handleKeydown);
|
||||
this.cmdInput.addEventListener('keydown', this.handleCmdKeydown);
|
||||
this.editor.addEventListener('beforeinput', this.handleBeforeInput.bind(this));
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.editor.focus();
|
||||
}
|
||||
|
||||
setMode(mode) {
|
||||
this.mode = mode;
|
||||
this.modeIndicator.textContent = mode.toUpperCase();
|
||||
|
||||
// Update editor classes
|
||||
this.editor.classList.remove('insert-mode', 'visual-mode', 'normal-mode');
|
||||
this.editor.classList.add(`${mode}-mode`);
|
||||
|
||||
if (mode === 'visual') {
|
||||
this.visualStartOffset = this.getCaretOffset();
|
||||
this.editor.addEventListener('selectionchange', this.updateVisualSelection);
|
||||
} else {
|
||||
this.clearVisualSelection();
|
||||
this.editor.removeEventListener('selectionchange', this.updateVisualSelection);
|
||||
}
|
||||
|
||||
if (mode === 'command') {
|
||||
this.cmdLine.style.display = 'block';
|
||||
this.cmdInput.value = '';
|
||||
this.cmdInput.focus();
|
||||
} else {
|
||||
this.cmdLine.style.display = 'none';
|
||||
if (mode !== 'insert') {
|
||||
// Keep focus on editor for all non-insert modes
|
||||
this.editor.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCaretOffset() {
|
||||
let caretOffset = 0;
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
if (!sel || sel.rangeCount === 0) return 0;
|
||||
updateVisualSelection() {
|
||||
if (this.mode !== 'visual') return;
|
||||
this.visualEndOffset = this.getCaretOffset();
|
||||
}
|
||||
|
||||
const range = sel.getRangeAt(0);
|
||||
const preCaretRange = range.cloneRange();
|
||||
preCaretRange.selectNodeContents(this.editor);
|
||||
preCaretRange.setEnd(range.endContainer, range.endOffset);
|
||||
caretOffset = preCaretRange.toString().length;
|
||||
return caretOffset;
|
||||
clearVisualSelection() {
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
if (sel) sel.removeAllRanges();
|
||||
this.visualStartOffset = null;
|
||||
this.visualEndOffset = null;
|
||||
}
|
||||
|
||||
getCaretOffset() {
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
if (!sel || sel.rangeCount === 0) return 0;
|
||||
|
||||
const range = sel.getRangeAt(0);
|
||||
const preCaretRange = range.cloneRange();
|
||||
preCaretRange.selectNodeContents(this.editor);
|
||||
preCaretRange.setEnd(range.endContainer, range.endOffset);
|
||||
return preCaretRange.toString().length;
|
||||
}
|
||||
|
||||
setCaretOffset(offset) {
|
||||
const textContent = this.editor.innerText;
|
||||
offset = Math.max(0, Math.min(offset, textContent.length));
|
||||
|
||||
const range = document.createRange();
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
const walker = document.createTreeWalker(
|
||||
this.editor,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
null,
|
||||
false
|
||||
);
|
||||
|
||||
let currentOffset = 0;
|
||||
let node;
|
||||
|
||||
while ((node = walker.nextNode())) {
|
||||
const nodeLength = node.textContent.length;
|
||||
if (currentOffset + nodeLength >= offset) {
|
||||
range.setStart(node, offset - currentOffset);
|
||||
range.collapse(true);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
return;
|
||||
}
|
||||
currentOffset += nodeLength;
|
||||
}
|
||||
|
||||
setCaretOffset(offset) {
|
||||
const range = document.createRange();
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
const walker = document.createTreeWalker(this.editor, NodeFilter.SHOW_TEXT, null, false);
|
||||
// If we couldn't find the position, set to end
|
||||
if (this.editor.lastChild) {
|
||||
range.selectNodeContents(this.editor.lastChild);
|
||||
range.collapse(false);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
}
|
||||
|
||||
let currentOffset = 0;
|
||||
let node;
|
||||
while ((node = walker.nextNode())) {
|
||||
if (currentOffset + node.length >= offset) {
|
||||
range.setStart(node, offset - currentOffset);
|
||||
range.collapse(true);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
return;
|
||||
}
|
||||
currentOffset += node.length;
|
||||
handleBeforeInput(e) {
|
||||
if (this.mode !== 'insert') {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
handleCmdKeydown(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
this.executeCommand(this.cmdInput.value);
|
||||
this.setMode('normal');
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.setMode('normal');
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(cmd) {
|
||||
const trimmedCmd = cmd.trim();
|
||||
|
||||
// Handle basic vim commands
|
||||
if (trimmedCmd === 'w' || trimmedCmd === 'write') {
|
||||
console.log('Save command (not implemented)');
|
||||
} else if (trimmedCmd === 'q' || trimmedCmd === 'quit') {
|
||||
console.log('Quit command (not implemented)');
|
||||
} else if (trimmedCmd === 'wq' || trimmedCmd === 'x') {
|
||||
console.log('Save and quit command (not implemented)');
|
||||
} else if (/^\d+$/.test(trimmedCmd)) {
|
||||
// Go to line number
|
||||
const lineNum = parseInt(trimmedCmd, 10) - 1;
|
||||
this.goToLine(lineNum);
|
||||
}
|
||||
}
|
||||
|
||||
goToLine(lineNum) {
|
||||
const lines = this.editor.innerText.split('\n');
|
||||
if (lineNum < 0 || lineNum >= lines.length) return;
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0; i < lineNum; i++) {
|
||||
offset += lines[i].length + 1;
|
||||
}
|
||||
this.setCaretOffset(offset);
|
||||
}
|
||||
|
||||
getCurrentLineInfo() {
|
||||
const text = this.editor.innerText;
|
||||
const caretPos = this.getCaretOffset();
|
||||
const lines = text.split('\n');
|
||||
|
||||
let charCount = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (caretPos <= charCount + lines[i].length) {
|
||||
return {
|
||||
lineIndex: i,
|
||||
lines: lines,
|
||||
lineStartOffset: charCount,
|
||||
positionInLine: caretPos - charCount
|
||||
};
|
||||
}
|
||||
charCount += lines[i].length + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
lineIndex: lines.length - 1,
|
||||
lines: lines,
|
||||
lineStartOffset: charCount - lines[lines.length - 1].length - 1,
|
||||
positionInLine: 0
|
||||
};
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
if (this.mode === 'insert') {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.setMode('normal');
|
||||
// Move cursor one position left (vim behavior)
|
||||
const offset = this.getCaretOffset();
|
||||
if (offset > 0) {
|
||||
this.setCaretOffset(offset - 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
handleKeydown(e) {
|
||||
const key = e.key;
|
||||
if (this.mode === 'command') {
|
||||
return; // Command mode input is handled by cmdInput
|
||||
}
|
||||
|
||||
if (this.mode === 'insert') {
|
||||
if (key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.mode = 'normal';
|
||||
this.editor.blur();
|
||||
this.editor.focus();
|
||||
}
|
||||
return;
|
||||
if (this.mode === 'visual') {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.setMode('normal');
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow movement in visual mode
|
||||
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
|
||||
return; // Let default behavior handle selection
|
||||
}
|
||||
|
||||
if (e.key === 'y') {
|
||||
e.preventDefault();
|
||||
// Yank selected text
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
this.yankedLine = sel.toString();
|
||||
}
|
||||
this.setMode('normal');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mode === 'command') {
|
||||
if (key === 'Enter' || key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.cmdLine.style.display = 'none';
|
||||
this.mode = 'normal';
|
||||
this.keyBuffer = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mode === 'visual') {
|
||||
if (key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.mode = 'normal';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle normal mode
|
||||
this.keyBuffer += key;
|
||||
|
||||
const text = this.editor.innerText;
|
||||
const caretPos = this.getCaretOffset();
|
||||
const lines = text.split('\n');
|
||||
|
||||
let charCount = 0, lineIdx = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (caretPos <= charCount + lines[i].length) {
|
||||
lineIdx = i;
|
||||
break;
|
||||
}
|
||||
charCount += lines[i].length + 1;
|
||||
}
|
||||
|
||||
const offsetToLine = idx =>
|
||||
text.split('\n').slice(0, idx).reduce((acc, l) => acc + l.length + 1, 0);
|
||||
|
||||
switch (this.keyBuffer) {
|
||||
case 'i':
|
||||
e.preventDefault();
|
||||
this.mode = 'insert';
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
e.preventDefault();
|
||||
this.mode = 'visual';
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case ':':
|
||||
e.preventDefault();
|
||||
this.mode = 'command';
|
||||
this.cmdLine.style.display = 'block';
|
||||
this.cmdLine.textContent = ':';
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'yy':
|
||||
e.preventDefault();
|
||||
this.yankedLine = lines[lineIdx];
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'dd':
|
||||
e.preventDefault();
|
||||
this.lastDeletedLine = lines[lineIdx];
|
||||
lines.splice(lineIdx, 1);
|
||||
this.editor.innerText = lines.join('\n');
|
||||
this.setCaretOffset(offsetToLine(lineIdx));
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
e.preventDefault();
|
||||
const lineToPaste = this.yankedLine || this.lastDeletedLine;
|
||||
if (lineToPaste) {
|
||||
lines.splice(lineIdx + 1, 0, lineToPaste);
|
||||
this.editor.innerText = lines.join('\n');
|
||||
this.setCaretOffset(offsetToLine(lineIdx + 1));
|
||||
}
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case '0':
|
||||
e.preventDefault();
|
||||
this.setCaretOffset(offsetToLine(lineIdx));
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case '$':
|
||||
e.preventDefault();
|
||||
this.setCaretOffset(offsetToLine(lineIdx) + lines[lineIdx].length);
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'gg':
|
||||
e.preventDefault();
|
||||
this.setCaretOffset(0);
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
e.preventDefault();
|
||||
this.setCaretOffset(text.length);
|
||||
this.keyBuffer = '';
|
||||
break;
|
||||
|
||||
case 'Escape':
|
||||
e.preventDefault();
|
||||
this.mode = 'normal';
|
||||
this.keyBuffer = '';
|
||||
this.cmdLine.style.display = 'none';
|
||||
break;
|
||||
|
||||
default:
|
||||
// allow up to 2 chars for combos
|
||||
if (this.keyBuffer.length > 2) this.keyBuffer = '';
|
||||
break;
|
||||
if (e.key === 'd' || e.key === 'x') {
|
||||
e.preventDefault();
|
||||
// Delete selected text
|
||||
const sel = this.shadowRoot.getSelection();
|
||||
if (sel && sel.rangeCount > 0) {
|
||||
this.lastDeletedLine = sel.toString();
|
||||
document.execCommand('delete');
|
||||
}
|
||||
this.setMode('normal');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('njet-editor', NjetEditor);
|
||||
export {NjetEditor}
|
||||
// Normal mode handling
|
||||
e.preventDefault();
|
||||
|
||||
// Special keys that should be handled immediately
|
||||
if (e.key === 'Escape') {
|
||||
this.keyBuffer = '';
|
||||
this.setMode('normal');
|
||||
return;
|
||||
}
|
||||
|
||||
// Build key buffer for commands
|
||||
this.keyBuffer += e.key;
|
||||
|
||||
const lineInfo = this.getCurrentLineInfo();
|
||||
const { lineIndex, lines, lineStartOffset, positionInLine } = lineInfo;
|
||||
|
||||
// Process commands
|
||||
switch (this.keyBuffer) {
|
||||
case 'i':
|
||||
this.keyBuffer = '';
|
||||
this.setMode('insert');
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(this.getCaretOffset() + 1);
|
||||
this.setMode('insert');
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
this.keyBuffer = '';
|
||||
this.setMode('visual');
|
||||
break;
|
||||
|
||||
case ':':
|
||||
this.keyBuffer = '';
|
||||
this.setMode('command');
|
||||
break;
|
||||
|
||||
case 'yy':
|
||||
this.keyBuffer = '';
|
||||
this.yankedLine = lines[lineIndex];
|
||||
break;
|
||||
|
||||
case 'dd':
|
||||
this.keyBuffer = '';
|
||||
this.lastDeletedLine = lines[lineIndex];
|
||||
lines.splice(lineIndex, 1);
|
||||
if (lines.length === 0) lines.push('');
|
||||
this.editor.innerText = lines.join('\n');
|
||||
this.setCaretOffset(lineStartOffset);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
this.keyBuffer = '';
|
||||
const lineToPaste = this.yankedLine || this.lastDeletedLine;
|
||||
if (lineToPaste) {
|
||||
lines.splice(lineIndex + 1, 0, lineToPaste);
|
||||
this.editor.innerText = lines.join('\n');
|
||||
this.setCaretOffset(lineStartOffset + lines[lineIndex].length + 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case '0':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(lineStartOffset);
|
||||
break;
|
||||
|
||||
case '$':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(lineStartOffset + lines[lineIndex].length);
|
||||
break;
|
||||
|
||||
case 'gg':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(0);
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(this.editor.innerText.length);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
case 'ArrowLeft':
|
||||
this.keyBuffer = '';
|
||||
const currentOffset = this.getCaretOffset();
|
||||
if (currentOffset > 0) {
|
||||
this.setCaretOffset(currentOffset - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
case 'ArrowRight':
|
||||
this.keyBuffer = '';
|
||||
this.setCaretOffset(this.getCaretOffset() + 1);
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
case 'ArrowDown':
|
||||
this.keyBuffer = '';
|
||||
if (lineIndex < lines.length - 1) {
|
||||
const nextLineStart = lineStartOffset + lines[lineIndex].length + 1;
|
||||
const nextLineLength = lines[lineIndex + 1].length;
|
||||
const newPosition = Math.min(positionInLine, nextLineLength);
|
||||
this.setCaretOffset(nextLineStart + newPosition);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case 'ArrowUp':
|
||||
this.keyBuffer = '';
|
||||
if (lineIndex > 0) {
|
||||
let prevLineStart = 0;
|
||||
for (let i = 0; i < lineIndex - 1; i++) {
|
||||
prevLineStart += lines[i].length + 1;
|
||||
}
|
||||
const prevLineLength = lines[lineIndex - 1].length;
|
||||
const newPosition = Math.min(positionInLine, prevLineLength);
|
||||
this.setCaretOffset(prevLineStart + newPosition);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Clear buffer if it gets too long or contains invalid sequences
|
||||
if (this.keyBuffer.length > 2 ||
|
||||
(this.keyBuffer.length === 2 && !['dd', 'yy', 'gg'].includes(this.keyBuffer))) {
|
||||
this.keyBuffer = '';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('njet-editor', NjetEditor);
|
||||
export { NjetEditor }
|
||||
|
||||
@ -5,64 +5,65 @@
|
||||
// The code seems to rely on some external dependencies like 'models.Message', 'app', and 'Schedule'. These should be imported or defined elsewhere in your application.
|
||||
|
||||
// MIT License: This is free software. Permission is granted to use, copy, modify, and/or distribute this software for any purpose with or without fee. The software is provided "as is" without any warranty.
|
||||
import {app} from "./app.js";
|
||||
import { app } from "./app.js";
|
||||
|
||||
const LONG_TIME = 1000 * 60 * 20
|
||||
const LONG_TIME = 1000 * 60 * 20;
|
||||
|
||||
export class ReplyEvent extends Event {
|
||||
constructor(messageTextTarget) {
|
||||
super('reply', { bubbles: true, composed: true });
|
||||
this.messageTextTarget = messageTextTarget;
|
||||
constructor(messageTextTarget) {
|
||||
super('reply', { bubbles: true, composed: true });
|
||||
this.messageTextTarget = messageTextTarget;
|
||||
|
||||
const newMessage = messageTextTarget.cloneNode(true);
|
||||
newMessage.style.maxHeight = "0"
|
||||
messageTextTarget.parentElement.insertBefore(newMessage, messageTextTarget);
|
||||
// Clone and sanitize message node to text-only reply
|
||||
const newMessage = messageTextTarget.cloneNode(true);
|
||||
newMessage.style.maxHeight = "0";
|
||||
messageTextTarget.parentElement.insertBefore(newMessage, messageTextTarget);
|
||||
|
||||
newMessage.querySelectorAll('.embed-url-link').forEach(link => {
|
||||
link.remove()
|
||||
})
|
||||
// Remove all .embed-url-link
|
||||
newMessage.querySelectorAll('.embed-url-link').forEach(link => link.remove());
|
||||
|
||||
newMessage.querySelectorAll('picture').forEach(picture => {
|
||||
const img = picture.querySelector('img');
|
||||
if (img) {
|
||||
picture.replaceWith(img);
|
||||
}
|
||||
})
|
||||
// Replace <picture> with their <img>
|
||||
newMessage.querySelectorAll('picture').forEach(picture => {
|
||||
const img = picture.querySelector('img');
|
||||
if (img) picture.replaceWith(img);
|
||||
});
|
||||
|
||||
newMessage.querySelectorAll('img').forEach(img => {
|
||||
const src = img.src || img.currentSrc;
|
||||
img.replaceWith(document.createTextNode(src));
|
||||
})
|
||||
// Replace <img> with just their src
|
||||
newMessage.querySelectorAll('img').forEach(img => {
|
||||
const src = img.src || img.currentSrc;
|
||||
img.replaceWith(document.createTextNode(src));
|
||||
});
|
||||
|
||||
newMessage.querySelectorAll('iframe').forEach(iframe => {
|
||||
const src = iframe.src || iframe.currentSrc;
|
||||
iframe.replaceWith(document.createTextNode(src));
|
||||
})
|
||||
// Replace <iframe> with their src
|
||||
newMessage.querySelectorAll('iframe').forEach(iframe => {
|
||||
const src = iframe.src || iframe.currentSrc;
|
||||
iframe.replaceWith(document.createTextNode(src));
|
||||
});
|
||||
|
||||
newMessage.querySelectorAll('a').forEach(a => {
|
||||
const href = a.getAttribute('href');
|
||||
const text = a.innerText || a.textContent;
|
||||
if (text === href || text === '') {
|
||||
a.replaceWith(document.createTextNode(href));
|
||||
} else {
|
||||
a.replaceWith(document.createTextNode(`[${text}](${href})`));
|
||||
}
|
||||
})
|
||||
// Replace <a> with href or markdown
|
||||
newMessage.querySelectorAll('a').forEach(a => {
|
||||
const href = a.getAttribute('href');
|
||||
const text = a.innerText || a.textContent;
|
||||
if (text === href || text === '') {
|
||||
a.replaceWith(document.createTextNode(href));
|
||||
} else {
|
||||
a.replaceWith(document.createTextNode(`[${text}](${href})`));
|
||||
}
|
||||
});
|
||||
|
||||
this.replyText = newMessage.innerText.replaceAll("\n\n", "\n").trim();
|
||||
newMessage.remove()
|
||||
}
|
||||
this.replyText = newMessage.innerText.replaceAll("\n\n", "\n").trim();
|
||||
newMessage.remove();
|
||||
}
|
||||
}
|
||||
|
||||
class MessageElement extends HTMLElement {
|
||||
// static observedAttributes = ['data-uid', 'data-color', 'data-channel_uid', 'data-user_nick', 'data-created_at', 'data-user_uid'];
|
||||
|
||||
updateUI() {
|
||||
if (this._originalChildren === undefined) {
|
||||
const { color, user_nick, created_at, user_uid} = this.dataset;
|
||||
const { color, user_nick, created_at, user_uid } = this.dataset;
|
||||
this.classList.add('message');
|
||||
this.style.maxWidth = '100%';
|
||||
this._originalChildren = Array.from(this.children);
|
||||
|
||||
this.innerHTML = `
|
||||
<a class="avatar" style="background-color: ${color || ''}; color: black;" href="/user/${user_uid || ''}.html">
|
||||
<img class="avatar-img" width="40" height="40" src="/avatar/${user_uid || ''}.svg" alt="${user_nick || ''}" loading="lazy">
|
||||
@ -72,27 +73,28 @@ class MessageElement extends HTMLElement {
|
||||
<div class="text"></div>
|
||||
<div class="time no-select" data-created_at="${created_at || ''}">
|
||||
<span></span>
|
||||
<a href="#reply">reply</a></div>
|
||||
<a href="#reply">reply</a>
|
||||
</div>
|
||||
`;
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.messageDiv = this.querySelector('.text');
|
||||
|
||||
if (this._originalChildren && this._originalChildren.length > 0) {
|
||||
this._originalChildren.forEach(child => {
|
||||
this.messageDiv.appendChild(child);
|
||||
});
|
||||
this._originalChildren.forEach(child => {
|
||||
this.messageDiv.appendChild(child);
|
||||
});
|
||||
}
|
||||
|
||||
this.timeDiv = this.querySelector('.time span');
|
||||
this.replyDiv = this.querySelector('.time a');
|
||||
|
||||
this.replyDiv.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.dispatchEvent(new ReplyEvent(this.messageDiv));
|
||||
})
|
||||
this.replyDiv.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.dispatchEvent(new ReplyEvent(this.messageDiv));
|
||||
});
|
||||
}
|
||||
|
||||
// Sibling logic for user switches and long time gaps
|
||||
if ((!this.siblingGenerated || this.siblingGenerated !== this.nextElementSibling) && this.nextElementSibling) {
|
||||
this.siblingGenerated = this.nextElementSibling;
|
||||
if (this.nextElementSibling?.dataset?.user_uid !== this.dataset.user_uid) {
|
||||
@ -102,7 +104,7 @@ class MessageElement extends HTMLElement {
|
||||
const siblingTime = new Date(this.nextElementSibling.dataset.created_at);
|
||||
const currentTime = new Date(this.dataset.created_at);
|
||||
|
||||
if (currentTime.getTime() - siblingTime.getTime() > LONG_TIME) {
|
||||
if (currentTime.getTime() - siblingTime.getTime() > LONG_TIME) {
|
||||
this.classList.add('long-time');
|
||||
} else {
|
||||
this.classList.remove('long-time');
|
||||
@ -115,7 +117,7 @@ class MessageElement extends HTMLElement {
|
||||
|
||||
updateMessage(...messages) {
|
||||
if (this._originalChildren) {
|
||||
this.messageDiv.replaceChildren(...messages)
|
||||
this.messageDiv.replaceChildren(...messages);
|
||||
this._originalChildren = messages;
|
||||
}
|
||||
}
|
||||
@ -124,38 +126,26 @@ class MessageElement extends HTMLElement {
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
}
|
||||
|
||||
connectedMoveCallback() {
|
||||
}
|
||||
disconnectedCallback() {}
|
||||
connectedMoveCallback() {}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
this.updateUI()
|
||||
this.updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
class MessageList extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
app.ws.addEventListener("update_message_text", (data) => {
|
||||
if (this.messageMap.has(data.uid)) {
|
||||
this.upsertMessage(data);
|
||||
}
|
||||
});
|
||||
app.ws.addEventListener("set_typing", (data) => {
|
||||
this.triggerGlow(data.user_uid,data.color);
|
||||
});
|
||||
|
||||
this.messageMap = new Map();
|
||||
this.visibleSet = new Set();
|
||||
|
||||
this._observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
this.visibleSet.add(entry.target);
|
||||
const messageElement = entry.target;
|
||||
if (messageElement instanceof MessageElement) {
|
||||
messageElement.updateUI();
|
||||
if (entry.target instanceof MessageElement) {
|
||||
entry.target.updateUI();
|
||||
}
|
||||
} else {
|
||||
this.visibleSet.delete(entry.target);
|
||||
@ -164,28 +154,42 @@ class MessageList extends HTMLElement {
|
||||
}, {
|
||||
root: this,
|
||||
threshold: 0,
|
||||
})
|
||||
});
|
||||
|
||||
// End-of-messages marker
|
||||
this.endOfMessages = document.createElement('div');
|
||||
this.endOfMessages.classList.add('message-list-bottom');
|
||||
this.prepend(this.endOfMessages);
|
||||
|
||||
for(const c of this.children) {
|
||||
// Observe existing children and index by uid
|
||||
for (const c of this.children) {
|
||||
this._observer.observe(c);
|
||||
if (c instanceof MessageElement) {
|
||||
this.messageMap.set(c.dataset.uid, c);
|
||||
this.messageMap.set(c.dataset.uid, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Wire up socket events
|
||||
app.ws.addEventListener("update_message_text", (data) => {
|
||||
if (this.messageMap.has(data.uid)) {
|
||||
this.upsertMessage(data);
|
||||
}
|
||||
});
|
||||
app.ws.addEventListener("set_typing", (data) => {
|
||||
this.triggerGlow(data.user_uid, data.color);
|
||||
});
|
||||
|
||||
this.scrollToBottom(true);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.addEventListener('click', (e) => {
|
||||
if (e.target.tagName !== 'IMG' || e.target.classList.contains('avatar-img')) return;
|
||||
if (
|
||||
e.target.tagName !== 'IMG' ||
|
||||
e.target.classList.contains('avatar-img')
|
||||
) return;
|
||||
|
||||
const img = e.target;
|
||||
|
||||
const overlay = document.createElement('div');
|
||||
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);display:flex;justify-content:center;align-items:center;z-index:9999;cursor:pointer;';
|
||||
|
||||
@ -206,12 +210,11 @@ class MessageList extends HTMLElement {
|
||||
|
||||
overlay.appendChild(fullImg);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
overlay.addEventListener('click', () => {
|
||||
if (overlay.parentNode) {
|
||||
overlay.parentNode.removeChild(overlay);
|
||||
}
|
||||
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
||||
});
|
||||
// Optional: ESC key closes overlay
|
||||
// ESC to close
|
||||
const escListener = (evt) => {
|
||||
if (evt.key === 'Escape') {
|
||||
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
||||
@ -219,8 +222,9 @@ class MessageList extends HTMLElement {
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', escListener);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
isElementVisible(element) {
|
||||
if (!element) return false;
|
||||
const rect = element.getBoundingClientRect();
|
||||
@ -231,10 +235,12 @@ class MessageList extends HTMLElement {
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
isScrolledToBottom() {
|
||||
return this.visibleSet.has(this.endOfMessages)
|
||||
return this.visibleSet.has(this.endOfMessages);
|
||||
}
|
||||
scrollToBottom(force = false, behavior= 'instant') {
|
||||
|
||||
scrollToBottom(force = false, behavior = 'instant') {
|
||||
if (force || !this.isScrolledToBottom()) {
|
||||
this.endOfMessages.scrollIntoView({ behavior, block: 'end' });
|
||||
setTimeout(() => {
|
||||
@ -261,40 +267,44 @@ class MessageList extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateTimes() {
|
||||
this.visibleSet.forEach((messageElement) => {
|
||||
if (messageElement instanceof MessageElement) {
|
||||
messageElement.updateUI();
|
||||
}
|
||||
})
|
||||
if (messageElement instanceof MessageElement) {
|
||||
messageElement.updateUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
upsertMessage(data) {
|
||||
let message = this.messageMap.get(data.uid);
|
||||
if (message) {
|
||||
message.parentElement?.removeChild(message);
|
||||
}
|
||||
if (message && (data.is_final || !data.message)) {
|
||||
message.parentElement?.removeChild(message);
|
||||
// TO force insert
|
||||
message = null;
|
||||
|
||||
if (!data.message) return
|
||||
}
|
||||
if (!data.message) return;
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
|
||||
wrapper.innerHTML = data.html;
|
||||
|
||||
if (message) {
|
||||
// If the old element is already custom, only update its message children
|
||||
message.updateMessage(...(wrapper.firstElementChild._originalChildren || wrapper.firstElementChild.children));
|
||||
} else {
|
||||
// If not, insert the new one and observe
|
||||
message = wrapper.firstElementChild;
|
||||
this.messageMap.set(data.uid, message);
|
||||
this._observer.observe(message);
|
||||
this.endOfMessages.after(message);
|
||||
}
|
||||
|
||||
const scrolledToBottom = this.isScrolledToBottom();
|
||||
this.endOfMessages.after(message);
|
||||
|
||||
if (scrolledToBottom) this.scrollToBottom(true);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("chat-message", MessageElement);
|
||||
customElements.define("message-list", MessageList);
|
||||
|
||||
|
||||
1207
src/snek/system/ads.py
Normal file
1207
src/snek/system/ads.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
DEFAULT_LIMIT = 30
|
||||
import asyncio
|
||||
import typing
|
||||
|
||||
import traceback
|
||||
from snek.system.model import BaseModel
|
||||
|
||||
|
||||
@ -51,7 +51,9 @@ class BaseMapper:
|
||||
kwargs["uid"] = uid
|
||||
if not kwargs.get("deleted_at"):
|
||||
kwargs["deleted_at"] = None
|
||||
record = await self.run_in_executor(self.table.find_one, **kwargs)
|
||||
#traceback.print_exc()
|
||||
|
||||
record = await self.db.get(self.table_name, kwargs)
|
||||
if not record:
|
||||
return None
|
||||
record = dict(record)
|
||||
@ -61,23 +63,29 @@ class BaseMapper:
|
||||
return model
|
||||
|
||||
async def exists(self, **kwargs):
|
||||
return await self.run_in_executor(self.table.exists, **kwargs)
|
||||
return await self.db.count(self.table_name, kwargs)
|
||||
|
||||
|
||||
#return await self.run_in_executor(self.table.exists, **kwargs)
|
||||
|
||||
async def count(self, **kwargs) -> int:
|
||||
return await self.run_in_executor(self.table.count, **kwargs)
|
||||
return await self.db.count(self.table_name,kwargs)
|
||||
|
||||
|
||||
async def save(self, model: BaseModel) -> bool:
|
||||
if not model.record.get("uid"):
|
||||
raise Exception(f"Attempt to save without uid: {model.record}.")
|
||||
model.updated_at.update()
|
||||
return await self.run_in_executor(self.table.upsert, model.record, ["uid"],use_semaphore=True)
|
||||
await self.upsert(model)
|
||||
return model
|
||||
#return await self.run_in_executor(self.table.upsert, model.record, ["uid"],use_semaphore=True)
|
||||
|
||||
async def find(self, **kwargs) -> typing.AsyncGenerator:
|
||||
if not kwargs.get("_limit"):
|
||||
kwargs["_limit"] = self.default_limit
|
||||
if not kwargs.get("deleted_at"):
|
||||
kwargs["deleted_at"] = None
|
||||
for record in await self.run_in_executor(self.table.find, **kwargs):
|
||||
for record in await self.db.find(self.table_name, kwargs):
|
||||
model = await self.new()
|
||||
for key, value in record.items():
|
||||
model[key] = value
|
||||
@ -88,21 +96,21 @@ class BaseMapper:
|
||||
return "insert" in sql or "update" in sql or "delete" in sql
|
||||
|
||||
async def query(self, sql, *args):
|
||||
for record in await self.run_in_executor(self.db.query, sql, *args, use_semaphore=await self._use_semaphore(sql)):
|
||||
for record in await self.db.query(sql, *args):
|
||||
yield dict(record)
|
||||
|
||||
async def update(self, model):
|
||||
if not model["deleted_at"] is None:
|
||||
raise Exception("Can't update deleted record.")
|
||||
model.updated_at.update()
|
||||
return await self.run_in_executor(self.table.update, model.record, ["uid"],use_semaphore=True)
|
||||
return await self.db.update(self.table_name, model.record, {"uid": model["uid"]})
|
||||
|
||||
async def upsert(self, model):
|
||||
model.updated_at.update()
|
||||
return await self.run_in_executor(self.table.upsert, model.record, ["uid"],use_semaphore=True)
|
||||
await self.db.upsert(self.table_name, model.record, {"uid": model["uid"]})
|
||||
return model
|
||||
|
||||
async def delete(self, **kwargs) -> int:
|
||||
if not kwargs or not isinstance(kwargs, dict):
|
||||
raise Exception("Can't execute delete with no filter.")
|
||||
kwargs["use_semaphore"] = True
|
||||
return await self.run_in_executor(self.table.delete, **kwargs)
|
||||
return await self.db.delete(self.table_name, kwargs)
|
||||
|
||||
@ -50,7 +50,7 @@ class BaseService:
|
||||
if result and result.__class__ == self.mapper.model_class:
|
||||
return result
|
||||
kwargs["uid"] = uid
|
||||
|
||||
print(kwargs,"ZZZZZZZ")
|
||||
result = await self.mapper.get(**kwargs)
|
||||
if result:
|
||||
await self.cache.set(result["uid"], result)
|
||||
|
||||
@ -29,7 +29,7 @@ class ChannelDriveApiView(DriveApiView):
|
||||
|
||||
class ChannelAttachmentView(BaseView):
|
||||
|
||||
login_required=True
|
||||
login_required=False
|
||||
|
||||
async def get(self):
|
||||
relative_path = self.request.match_info.get("relative_url")
|
||||
|
||||
@ -11,7 +11,7 @@ import asyncio
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
import random
|
||||
from aiohttp import web
|
||||
|
||||
from snek.system.model import now
|
||||
|
||||
@ -38,6 +38,10 @@ class WebView(BaseView):
|
||||
channel = await self.services.channel.get(
|
||||
uid=self.request.match_info.get("channel")
|
||||
)
|
||||
print(self.session.get("uid"),"ZZZZZZZZZZ")
|
||||
qq = await self.services.user.get(uid=self.session.get("uid"))
|
||||
|
||||
print("GGGGGGGGGG",qq)
|
||||
if not channel:
|
||||
user = await self.services.user.get(
|
||||
uid=self.request.match_info.get("channel")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user