Compare commits
4 Commits
ed33bee7ce
...
6afc50eb81
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6afc50eb81 | ||
|
|
78b19a03a8 | ||
|
|
037d525905 | ||
|
|
7cfb20e1c3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ __pycache__
|
|||||||
.venv
|
.venv
|
||||||
*.db
|
*.db
|
||||||
.envrc
|
.envrc
|
||||||
|
exec_folder/
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from fastapi.exceptions import HTTPException
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from model import Log, SessionLocal, Script
|
from model import Log, SessionLocal, Script
|
||||||
|
from run_scripts import run_scripts
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -28,15 +29,22 @@ class ScriptCreate(ScriptBase):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptUpdate(ScriptBase):
|
||||||
|
enabled: bool
|
||||||
|
|
||||||
|
|
||||||
class ScriptResponse(ScriptBase):
|
class ScriptResponse(ScriptBase):
|
||||||
id: int
|
id: int
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
enabled: bool
|
||||||
|
|
||||||
model_config = {"from_attributes": True}
|
model_config = {"from_attributes": True}
|
||||||
|
|
||||||
|
|
||||||
class ScriptLogCreate(BaseModel):
|
class ScriptLogCreate(BaseModel):
|
||||||
message: str
|
message: str
|
||||||
|
error_code: int
|
||||||
|
error_message: str
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
@@ -80,19 +88,25 @@ def delete_script(script_id: int):
|
|||||||
if not script:
|
if not script:
|
||||||
raise HTTPException(status_code=404, detail="Script not found")
|
raise HTTPException(status_code=404, detail="Script not found")
|
||||||
db.delete(script)
|
db.delete(script)
|
||||||
|
logs = db.query(Log).filter(Log.script_id == script_id).all()
|
||||||
|
for log in logs:
|
||||||
|
db.delete(log)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.close()
|
db.close()
|
||||||
return {"message": "Script deleted"}
|
return {"message": "Script deleted"}
|
||||||
|
|
||||||
|
|
||||||
@app.put("/script/{script_id}", response_model=ScriptResponse)
|
@app.put("/script/{script_id}", response_model=ScriptResponse)
|
||||||
def update_script(script_id: int, script: ScriptCreate):
|
def update_script(script_id: int, script: ScriptUpdate):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
existing_script = db.query(Script).filter(Script.id == script_id).first()
|
existing_script = db.query(Script).filter(Script.id == script_id).first()
|
||||||
if not existing_script:
|
if not existing_script:
|
||||||
raise HTTPException(status_code=404, detail="Script not found")
|
raise HTTPException(status_code=404, detail="Script not found")
|
||||||
existing_script.name = script.name
|
existing_script.name = script.name
|
||||||
existing_script.script_content = script.script_content
|
existing_script.script_content = script.script_content
|
||||||
|
existing_script.enabled = script.enabled
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(existing_script)
|
db.refresh(existing_script)
|
||||||
db.close()
|
db.close()
|
||||||
@@ -110,7 +124,12 @@ def get_script_logs(script_id: int):
|
|||||||
@app.post("/script/{script_id}/log")
|
@app.post("/script/{script_id}/log")
|
||||||
def create_script_log(script_id: int, log: ScriptLogCreate):
|
def create_script_log(script_id: int, log: ScriptLogCreate):
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
new_log = Log(script_id=script_id, message=log.message)
|
new_log = Log(
|
||||||
|
script_id=script_id,
|
||||||
|
message=log.message,
|
||||||
|
error_code=log.error_code,
|
||||||
|
error_message=log.error_message,
|
||||||
|
)
|
||||||
db.add(new_log)
|
db.add(new_log)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(new_log)
|
db.refresh(new_log)
|
||||||
@@ -130,6 +149,12 @@ def delete_script_log(script_id: int, log_id: int):
|
|||||||
return {"message": "Log deleted"}
|
return {"message": "Log deleted"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/script/{script_id}/execute")
|
||||||
|
def execute_script(script_id: int):
|
||||||
|
run_scripts([script_id])
|
||||||
|
return {"run_script": True}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
def health_check():
|
def health_check():
|
||||||
return {"status": "healthy"}
|
return {"status": "healthy"}
|
||||||
|
|||||||
0
backend/exec_folder/.gitkeep
Normal file
0
backend/exec_folder/.gitkeep
Normal file
@@ -3,6 +3,7 @@ from sqlalchemy.ext.declarative import declarative_base
|
|||||||
from sqlalchemy.orm import sessionmaker
|
from sqlalchemy.orm import sessionmaker
|
||||||
from sqlalchemy.sql.functions import func
|
from sqlalchemy.sql.functions import func
|
||||||
from sqlalchemy.sql.sqltypes import DateTime
|
from sqlalchemy.sql.sqltypes import DateTime
|
||||||
|
from sqlalchemy.types import Boolean
|
||||||
|
|
||||||
# Initialize the database
|
# Initialize the database
|
||||||
DATABASE_URL = "sqlite:///./project_monitor.db"
|
DATABASE_URL = "sqlite:///./project_monitor.db"
|
||||||
@@ -22,6 +23,7 @@ class Script(Base):
|
|||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
name = Column(String, nullable=False)
|
name = Column(String, nullable=False)
|
||||||
script_content = Column(Text, nullable=True)
|
script_content = Column(Text, nullable=True)
|
||||||
|
enabled = Column(Boolean, default=False)
|
||||||
created_at = Column(
|
created_at = Column(
|
||||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||||
)
|
)
|
||||||
@@ -32,6 +34,8 @@ class Log(Base):
|
|||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
message = Column(String, nullable=False)
|
message = Column(String, nullable=False)
|
||||||
|
error_code = Column(Integer, nullable=False, default=0)
|
||||||
|
error_message = Column(String, nullable=True)
|
||||||
created_at = Column(
|
created_at = Column(
|
||||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||||
)
|
)
|
||||||
|
|||||||
57
backend/run_scripts.py
Normal file
57
backend/run_scripts.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import model
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def run_scripts(script_ids: list[int] | None = None):
|
||||||
|
db = model.SessionLocal()
|
||||||
|
|
||||||
|
if script_ids:
|
||||||
|
scripts = db.query(model.Script).filter(model.Script.id.in_(script_ids)).all()
|
||||||
|
else:
|
||||||
|
scripts = db.query(model.Script).filter(model.Script.enabled).all()
|
||||||
|
|
||||||
|
for script in scripts:
|
||||||
|
print(f"Running script: {script.name}")
|
||||||
|
|
||||||
|
dump_script_to_file(script, f"exec_folder/{script.name}.py")
|
||||||
|
result = execute_script(f"exec_folder/{script.name}.py")
|
||||||
|
|
||||||
|
db.add(
|
||||||
|
model.Log(
|
||||||
|
script_id=script.id,
|
||||||
|
error_code=result.returncode,
|
||||||
|
message=result.stdout,
|
||||||
|
error_message=result.stderr,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
delete_script(f"exec_folder/{script.name}.py")
|
||||||
|
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def dump_script_to_file(script, filename):
|
||||||
|
with open(filename, "w") as file:
|
||||||
|
file.write(script.script_content)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_script(filename) -> subprocess.CompletedProcess:
|
||||||
|
result = subprocess.run(["python", filename], capture_output=True, text=True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def delete_script(filename):
|
||||||
|
try:
|
||||||
|
os.remove(filename)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
run_scripts()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
22
frontend/package-lock.json
generated
22
frontend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-python": "^6.2.1",
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@iconify/svelte": "^5.0.2",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"monaco-editor": "^0.54.0",
|
"monaco-editor": "^0.54.0",
|
||||||
"svelte-codemirror-editor": "^2.1.0"
|
"svelte-codemirror-editor": "^2.1.0"
|
||||||
@@ -803,6 +804,27 @@
|
|||||||
"url": "https://github.com/sponsors/nzakas"
|
"url": "https://github.com/sponsors/nzakas"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@iconify/svelte": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/svelte/-/svelte-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-1iWUT+1veS/QOAzKDG0NPgBtJYGoJqEPwF97voTm8jw6PQ6yU0hL73lEwFoTGMrZmatLvh9cjRBmeSHHaltmrg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/types": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/cyberalien"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"svelte": ">4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@iconify/types": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@isaacs/fs-minipass": {
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-python": "^6.2.1",
|
"@codemirror/lang-python": "^6.2.1",
|
||||||
|
"@iconify/svelte": "^5.0.2",
|
||||||
"codemirror": "^6.0.2",
|
"codemirror": "^6.0.2",
|
||||||
"monaco-editor": "^0.54.0",
|
"monaco-editor": "^0.54.0",
|
||||||
"svelte-codemirror-editor": "^2.1.0"
|
"svelte-codemirror-editor": "^2.1.0"
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export interface Log {
|
|||||||
id: number;
|
id: number;
|
||||||
script_id: number;
|
script_id: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
error_message: string;
|
||||||
|
error_code: number;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
}
|
}
|
||||||
export interface Script {
|
export interface Script {
|
||||||
@@ -27,6 +29,7 @@ export interface Script {
|
|||||||
name: string;
|
name: string;
|
||||||
script_content?: string;
|
script_content?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch all scripts
|
// Fetch all scripts
|
||||||
@@ -39,7 +42,9 @@ export async function fetchScripts(): Promise<Script[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a new script
|
// Add a new script
|
||||||
export async function addScript(script: Omit<Script, 'id' | 'created_at'>): Promise<Script> {
|
export async function addScript(
|
||||||
|
script: Omit<Script, 'id' | 'created_at' | 'enabled'>
|
||||||
|
): Promise<Script> {
|
||||||
const response = await fetch(`${API_URL}/script`, {
|
const response = await fetch(`${API_URL}/script`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -87,13 +92,13 @@ export async function fetchLogs(scriptId: number): Promise<Log[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a new log to a specific script
|
// Add a new log to a specific script
|
||||||
export async function addLog(scriptId: number, message: string): Promise<Log> {
|
export async function addLog(scriptId: number, log: Log): Promise<Log> {
|
||||||
const response = await fetch(`${API_URL}/script/${scriptId}/log`, {
|
const response = await fetch(`${API_URL}/script/${scriptId}/log`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ message })
|
body: JSON.stringify(log)
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to add log');
|
throw new Error('Failed to add log');
|
||||||
@@ -101,6 +106,17 @@ export async function addLog(scriptId: number, message: string): Promise<Log> {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute a script by ID
|
||||||
|
export async function executeScript(scriptId: number): Promise<{ message: string }> {
|
||||||
|
const response = await fetch(`${API_URL}/script/${scriptId}/execute`, {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to execute script');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
// Delete a log from a specific script
|
// Delete a log from a specific script
|
||||||
export async function deleteLog(scriptId: number, logId: number): Promise<void> {
|
export async function deleteLog(scriptId: number, logId: number): Promise<void> {
|
||||||
const response = await fetch(`${API_URL}/script/${scriptId}/log/${logId}`, {
|
const response = await fetch(`${API_URL}/script/${scriptId}/log/${logId}`, {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
|
import Icon from '@iconify/svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { checkHealth } from '$lib/api';
|
import { checkHealth } from '$lib/api';
|
||||||
@@ -64,14 +65,19 @@
|
|||||||
|
|
||||||
<div class="fixed bottom-4 left-4 group">
|
<div class="fixed bottom-4 left-4 group">
|
||||||
{#if $healthStatus === 'healthy'}
|
{#if $healthStatus === 'healthy'}
|
||||||
<span class="text-green-500">✅</span>
|
<Icon
|
||||||
|
icon="material-symbols:check-circle-rounded"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
class="text-green-400"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-full left-1/2 hidden group-hover:flex group-hover:w-max bg-green-500 text-white px-2 py-1 rounded shadow-lg"
|
class="absolute bottom-full left-1/2 hidden group-hover:flex group-hover:w-max bg-green-500 text-white px-2 py-1 rounded shadow-lg"
|
||||||
>
|
>
|
||||||
Connected to backend
|
Connected to backend
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="text-red-500">✘</span>
|
<Icon icon="ix:disconnected-circle-filled" width="24" height="24" class="text-red-400" />
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-full left-1/2 hidden group-hover:flex group-hover:w-max bg-red-500 text-white px-2 py-1 rounded shadow-lg"
|
class="absolute bottom-full left-1/2 hidden group-hover:flex group-hover:w-max bg-red-500 text-white px-2 py-1 rounded shadow-lg"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
{#each scripts as script (script.id)}
|
{#each scripts as script (script.id)}
|
||||||
<a
|
<a
|
||||||
href={`/scripts/${script.id}`}
|
href={`/scripts/${script.id}`}
|
||||||
class="block p-4 border rounded shadow bg-white hover:bg-gray-100"
|
class="block p-4 border rounded bg-white hover:bg-gray-100 {script.enabled
|
||||||
|
? 'shadow-lg shadow-green-500/50'
|
||||||
|
: 'shadow-lg shadow-red-500/50'}"
|
||||||
>
|
>
|
||||||
<h2 class="text-lg font-semibold text-gray-800">{script.name}</h2>
|
<h2 class="text-lg font-semibold text-gray-800">{script.name}</h2>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { updateScript, deleteScript, addLog, deleteLog } from '$lib/api';
|
import { updateScript, deleteScript, addLog, deleteLog, executeScript } from '$lib/api';
|
||||||
import type { Script, Log } from '$lib/api';
|
import type { Script, Log } from '$lib/api';
|
||||||
import CodeMirror from 'svelte-codemirror-editor';
|
import CodeMirror from 'svelte-codemirror-editor';
|
||||||
import { python } from '@codemirror/lang-python';
|
import { python } from '@codemirror/lang-python';
|
||||||
@@ -9,17 +9,44 @@
|
|||||||
let logs: Log[] = data.logs;
|
let logs: Log[] = data.logs;
|
||||||
let updatedTitle: string = script.name || '';
|
let updatedTitle: string = script.name || '';
|
||||||
let updatedContent: string = script.script_content || '';
|
let updatedContent: string = script.script_content || '';
|
||||||
|
let updatedEnabled: boolean = script.enabled || false;
|
||||||
|
|
||||||
let isEditMode: boolean = false;
|
let isEditMode: boolean = false;
|
||||||
|
|
||||||
let newLogMessage: string = '';
|
let newLog: Omit<Log, 'id' | 'script_id'> = {
|
||||||
|
message: '',
|
||||||
|
error_code: 0,
|
||||||
|
error_message: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
let selectedLog: Log | null = null;
|
||||||
|
|
||||||
|
function openLogPopup(log: Log) {
|
||||||
|
selectedLog = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLogPopup() {
|
||||||
|
selectedLog = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Notifications are now handled globally via the layout
|
// Notifications are now handled globally via the layout
|
||||||
|
|
||||||
|
async function handleExecuteScript() {
|
||||||
|
try {
|
||||||
|
await executeScript(script.id);
|
||||||
|
window.showNotification('success', 'Script executed successfully!');
|
||||||
|
} catch (err) {
|
||||||
|
window.showNotification('error', 'Failed to execute script. ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleUpdateScript() {
|
async function handleUpdateScript() {
|
||||||
if (script) {
|
if (script) {
|
||||||
try {
|
try {
|
||||||
const updatedScript = await updateScript(script.id, {
|
const updatedScript = await updateScript(script.id, {
|
||||||
name: updatedTitle,
|
name: updatedTitle,
|
||||||
script_content: updatedContent
|
script_content: updatedContent,
|
||||||
|
enabled: updatedEnabled
|
||||||
});
|
});
|
||||||
script = updatedScript;
|
script = updatedScript;
|
||||||
window.showNotification('success', 'Script updated successfully!');
|
window.showNotification('success', 'Script updated successfully!');
|
||||||
@@ -31,11 +58,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleAddLog() {
|
async function handleAddLog() {
|
||||||
if (newLogMessage.trim()) {
|
if (newLog.message.trim()) {
|
||||||
try {
|
try {
|
||||||
const newLog = await addLog(script.id, newLogMessage);
|
const addedLog = await addLog(script.id, newLog);
|
||||||
logs = [newLog, ...logs];
|
logs = [addedLog, ...logs];
|
||||||
newLogMessage = '';
|
newLog = {
|
||||||
|
message: '',
|
||||||
|
error_code: 0,
|
||||||
|
error_message: ''
|
||||||
|
};
|
||||||
window.showNotification('success', 'Log added successfully!');
|
window.showNotification('success', 'Log added successfully!');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
window.showNotification('error', 'Failed to add log. ' + err);
|
window.showNotification('error', 'Failed to add log. ' + err);
|
||||||
@@ -77,7 +108,19 @@
|
|||||||
required
|
required
|
||||||
class="text-2xl font-bold mb-4 w-full p-2 border rounded"
|
class="text-2xl font-bold mb-4 w-full p-2 border rounded"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CodeMirror bind:value={updatedContent} lang={python()} />
|
<CodeMirror bind:value={updatedContent} lang={python()} />
|
||||||
|
|
||||||
|
<div class="mt-4 flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
id="enabled"
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={updatedEnabled}
|
||||||
|
class="h-5 w-5 text-blue-500 border-gray-300 rounded focus:ring focus:ring-blue-200"
|
||||||
|
/>
|
||||||
|
<label for="enabled" class="text-sm font-medium text-gray-700">Enabled</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||||
on:click={handleUpdateScript}
|
on:click={handleUpdateScript}
|
||||||
@@ -98,51 +141,149 @@
|
|||||||
<h1 class="text-2xl font-bold mb-4">{script.name}</h1>
|
<h1 class="text-2xl font-bold mb-4">{script.name}</h1>
|
||||||
<pre
|
<pre
|
||||||
class="w-full p-2 border rounded font-mono text-sm bg-gray-100">{script.script_content}</pre>
|
class="w-full p-2 border rounded font-mono text-sm bg-gray-100">{script.script_content}</pre>
|
||||||
<button
|
<div class="mt-4 flex items-center space-x-2">
|
||||||
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
<span class="text-sm font-medium text-gray-700">Enabled:</span>
|
||||||
on:click={() => (isEditMode = true)}
|
<span
|
||||||
>
|
class="px-2 py-1 text-xs font-semibold rounded"
|
||||||
Edit Script
|
class:bg-green-100={script.enabled}
|
||||||
</button>
|
class:bg-red-100={!script.enabled}
|
||||||
<button
|
class:text-green-700={script.enabled}
|
||||||
class="mt-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
class:text-red-700={!script.enabled}
|
||||||
on:click={handleDeleteScript}
|
>
|
||||||
>
|
{script.enabled ? 'Yes' : 'No'}
|
||||||
Delete Script
|
</span>
|
||||||
</button>
|
</div>
|
||||||
|
<div class="mt-4 flex justify-between">
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
||||||
|
on:click={handleExecuteScript}
|
||||||
|
aria-label="Execute the script"
|
||||||
|
>
|
||||||
|
Execute Script
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||||
|
on:click={() => (isEditMode = true)}
|
||||||
|
>
|
||||||
|
Edit Script
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
||||||
|
on:click={handleDeleteScript}
|
||||||
|
>
|
||||||
|
Delete Script
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section class="mt-8">
|
<section class="mt-8">
|
||||||
<h2 class="text-xl font-bold mb-4">Logs</h2>
|
<h2 class="text-xl font-bold mb-4">Logs</h2>
|
||||||
<form on:submit|preventDefault={handleAddLog} class="mb-4">
|
<!--- --
|
||||||
<input
|
<form on:submit|preventDefault={handleAddLog} class="mb-4 space-y-4">
|
||||||
type="text"
|
<div>
|
||||||
bind:value={newLogMessage}
|
<label for="logMessage" class="block text-sm font-medium">Log Message</label>
|
||||||
placeholder="Enter new log message"
|
<input
|
||||||
class="w-full p-2 border rounded mb-2"
|
id="logMessage"
|
||||||
required
|
type="text"
|
||||||
/>
|
bind:value={newLog.message}
|
||||||
|
placeholder="Enter new log message"
|
||||||
|
class="w-full p-2 border rounded"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="errorCode" class="block text-sm font-medium">Error Code</label>
|
||||||
|
<input
|
||||||
|
id="errorCode"
|
||||||
|
type="number"
|
||||||
|
bind:value={newLog.error_code}
|
||||||
|
placeholder="Enter error code (0 for no error)"
|
||||||
|
class="w-full p-2 border rounded"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="errorMessage" class="block text-sm font-medium">Error Message</label>
|
||||||
|
<textarea
|
||||||
|
id="errorMessage"
|
||||||
|
type="text"
|
||||||
|
bind:value={newLog.error_message}
|
||||||
|
placeholder="Enter error message (optional)"
|
||||||
|
class="w-full p-2 border rounded"
|
||||||
|
>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
<button type="submit" class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600">
|
<button type="submit" class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600">
|
||||||
Add Log
|
Add Log
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
-->
|
||||||
<ul class="space-y-4">
|
<ul class="space-y-4">
|
||||||
{#each logs as log (log.id)}
|
{#each logs as log (log.id)}
|
||||||
<li class="p-4 border rounded bg-gray-50 flex justify-between items-center">
|
<li
|
||||||
<div>
|
class="p-2 rounded flex justify-between items-center border-s-slate-400 border-1"
|
||||||
<p class="text-sm text-gray-700">{log.message}</p>
|
class:bg-red-100={log.error_code !== 0}
|
||||||
<p class="text-xs text-gray-500">{log.created_at}</p>
|
class:bg-gray-50={log.error_code === 0}
|
||||||
</div>
|
>
|
||||||
|
<button class="p-2 w-full" on:click={() => openLogPopup(log)}>
|
||||||
|
<div class="z">
|
||||||
|
<p class="text-sm text-left">
|
||||||
|
{log.error_code !== 0
|
||||||
|
? log.error_message.split('\n')[0]
|
||||||
|
: log.message.split('\n')[0]}
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-gray-500 text-left">{log.created_at}</p>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600"
|
class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600 block"
|
||||||
on:click={() => handleDeleteLog(log.id)}
|
on:click|stopPropagation={() => handleDeleteLog(log.id)}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{#if selectedLog}
|
||||||
|
<div
|
||||||
|
class="fixed inset-0 bg-opacity-30 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
>
|
||||||
|
<div class="bg-white p-6 rounded shadow-lg w-3/4 max-w-2xl">
|
||||||
|
<h3 class="text-lg font-bold mb-4">Log Details</h3>
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="font-semibold">Message:</p>
|
||||||
|
<pre
|
||||||
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.message}</pre>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="font-semibold">Error Code:</p>
|
||||||
|
<pre
|
||||||
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.error_code}</pre>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="font-semibold">Error Message:</p>
|
||||||
|
<pre
|
||||||
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.error_message ||
|
||||||
|
'N/A'}</pre>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<p class="font-semibold">Created At:</p>
|
||||||
|
<pre
|
||||||
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.created_at}</pre>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="mt-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
||||||
|
on:click={closeLogPopup}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user