refactor: python -> go
All checks were successful
ci/woodpecker/tag/release Pipeline was successful

This commit is contained in:
Felipe M 2025-04-20 13:54:22 +02:00
parent 9c78ea2d48
commit 7c684af8c3
Signed by: fmartingr
GPG key ID: CCFBC5637D4000A8
79 changed files with 3594 additions and 3257 deletions

View file

@ -0,0 +1,123 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}} - ButterRobot Admin</title>
<link rel="stylesheet" href="https://unpkg.com/@tabler/core@latest/dist/css/tabler.min.css">
</head>
<body>
<div class="page">
<div class="sticky-top">
<header class="navbar navbar-expand-md navbar-light sticky-top d-print-none">
<div class="container-xl">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbar-menu">
<span class="navbar-toggler-icon"></span>
</button>
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pr-0 pr-md-3">
<a href="/admin/">
<h1>ButterRobot Admin</h1>
</a>
</h1>
<div class="navbar-nav flex-row order-md-last">
<div class="nav-item">
{{if not .LoggedIn}}
<a href="/admin/login">Log in</a>
{{else}}
<div class="d-none d-xl-block pl-2">
<div>{{.User.Username}} - <a class="mt-1 small"
href="/admin/logout">Log out</a></div>
</div>
</a>
{{end}}
</div>
</div>
</div>
</header>
{{if .LoggedIn}}
<div class="navbar-expand-md">
<div class="collapse navbar-collapse" id="navbar-menu">
<div class="navbar navbar-light">
<div class="container-xl">
<ul class="navbar-nav">
<li class="nav-item {{if contains .Path "/channels"}}active{{end}}">
<a class="nav-link" href="/admin/channels">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<line x1="5" y1="9" x2="19" y2="9" />
<line x1="5" y1="15" x2="19" y2="15" />
<line x1="11" y1="4" x2="7" y2="20" />
<line x1="17" y1="4" x2="13" y2="20" /></svg>
</span>
<span class="nav-link-title">
Channels
</span>
</a>
</li>
<li class="nav-item {{if contains .Path "/plugins"}}active{{end}}">
<a class="nav-link" href="/admin/plugins">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M4 7h3a1 1 0 0 0 1 -1v-1a2 2 0 0 1 4 0v1a1 1 0 0 0 1 1h3a1 1 0 0 1 1 1v3a1 1 0 0 0 1 1h1a2 2 0 0 1 0 4h-1a1 1 0 0 0 -1 1v3a1 1 0 0 1 -1 1h-3a1 1 0 0 1 -1 -1v-1a2 2 0 0 0 -4 0v1a1 1 0 0 1 -1 1h-3a1 1 0 0 1 -1 -1v-3a1 1 0 0 1 1 -1h1a2 2 0 0 0 0 -4h-1a1 1 0 0 1 -1 -1v-3a1 1 0 0 1 1 -1" />
</svg>
</span>
<span class="nav-link-title">
Plugins
</span>
</a>
</li>
<li class="nav-item {{if contains .Path "/channelplugins"}}active{{end}}">
<a class="nav-link" href="/admin/channelplugins">
<span class="nav-link-icon d-md-none d-lg-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M4 7h3a1 1 0 0 0 1 -1v-1a2 2 0 0 1 4 0v1a1 1 0 0 0 1 1h3a1 1 0 0 1 1 1v3a1 1 0 0 0 1 1h1a2 2 0 0 1 0 4h-1a1 1 0 0 0 -1 1v3a1 1 0 0 1 -1 1h-3a1 1 0 0 1 -1 -1v-1a2 2 0 0 0 -4 0v1a1 1 0 0 1 -1 1h-3a1 1 0 0 1 -1 -1v-3a1 1 0 0 1 1 -1h1a2 2 0 0 0 0 -4h-1a1 1 0 0 1 -1 -1v-3a1 1 0 0 1 1 -1" />
</svg>
</span>
<span class="nav-link-title">
Channel Plugins
</span>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
{{end}}
</div>
{{range .Flash}}
<div class="card">
<div class="card-status-top bg-{{.Category}}"></div>
<div class="card-body">
<p>{{.Message}}</p>
</div>
</div>
{{end}}
<div class="content">
<div class="container-xl">
{{template "content" .}}
</div>
</div>
</div>
<script src="https://unpkg.com/@tabler/core@latest/dist/js/tabler.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,114 @@
{{define "content"}}
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">Channel #{{.Channel.ID}}</h3>
</div>
<div class="card-body">
<form method="post" action="/admin/channels/{{.Channel.ID}}">
<div class="mb-3">
<label class="form-label">Platform</label>
<input type="text" class="form-control" value="{{.Channel.Platform}}" readonly>
</div>
<div class="mb-3">
<label class="form-label">Channel ID</label>
<input type="text" class="form-control" value="{{.Channel.PlatformChannelID}}" readonly>
</div>
<div class="mb-3">
<label class="form-label">Channel Name</label>
<input type="text" class="form-control" value="{{.Channel.ChannelName}}" readonly>
</div>
<div class="mb-3">
<label class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="enabled" value="true" {{if .Channel.Enabled}}checked{{end}}>
<span class="form-check-label">Channel Enabled</span>
</label>
<!-- Add a hidden field to ensure a value is sent even when checkbox is unchecked -->
<input type="hidden" name="form_submitted" value="true">
</div>
<div class="form-footer">
<button type="submit" class="btn btn-primary">Save</button>
<a href="/admin/channels" class="btn btn-link">Back to Channels</a>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">Channel Plugins</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>Plugin</th>
<th>Enabled</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range $pluginID, $channelPlugin := .Channel.Plugins}}
<tr>
<td>{{$pluginID}}</td>
<td>
{{if $channelPlugin.Enabled}}
<span class="badge bg-success">Enabled</span>
{{else}}
<span class="badge bg-danger">Disabled</span>
{{end}}
</td>
<td>
<form method="post" action="/admin/channelplugins/{{$channelPlugin.ID}}" class="d-inline">
<input type="hidden" name="enabled" value="{{if $channelPlugin.Enabled}}false{{else}}true{{end}}">
<button type="submit" class="btn btn-sm {{if $channelPlugin.Enabled}}btn-danger{{else}}btn-success{{end}}">
{{if $channelPlugin.Enabled}}Disable{{else}}Enable{{end}}
</button>
</form>
<form method="post" action="/admin/channelplugins/{{$channelPlugin.ID}}/delete" class="d-inline">
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Are you sure you want to remove this plugin?')">Remove</button>
</form>
</td>
</tr>
{{else}}
<tr>
<td colspan="3" class="text-center">No plugins for this channel</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<hr>
<h4>Add Plugin</h4>
<form method="post" action="/admin/channelplugins">
<input type="hidden" name="channel_id" value="{{.Channel.ID}}">
<div class="mb-3">
<label class="form-label">Plugin</label>
<select name="plugin_id" class="form-select" required>
<option value="">Select a plugin</option>
{{range $id, $plugin := .Plugins}}
<option value="{{$id}}">{{$plugin.GetName}} ({{$id}})</option>
{{end}}
</select>
</div>
<div class="mb-3">
<label class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="enabled" value="y">
<span class="form-check-label">Enable plugin</span>
</label>
</div>
<div class="form-footer">
<button type="submit" class="btn btn-primary">Add Plugin</button>
</div>
</form>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -0,0 +1,64 @@
{{define "content"}}
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Channels</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>ID</th>
<th>Platform</th>
<th>Channel ID</th>
<th>Name</th>
<th>Enabled</th>
<th>Plugins</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .Channels}}
<tr>
<td>{{.ID}}</td>
<td>{{.Platform}}</td>
<td>{{.PlatformChannelID}}</td>
<td>{{.ChannelName}}</td>
<td>
{{if .Enabled}}
<span class="badge bg-success">Enabled</span>
{{else}}
<span class="badge bg-danger">Disabled</span>
{{end}}
</td>
<td>
{{$count := len .Plugins}}
{{if eq $count 0}}
<span class="badge bg-yellow">No plugins</span>
{{else}}
<span class="badge bg-blue">{{$count}} plugins</span>
{{end}}
</td>
<td>
<a href="/admin/channels/{{.ID}}" class="btn btn-primary btn-sm">Edit</a>
<form method="post" action="/admin/channels/{{.ID}}/delete" class="d-inline">
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Are you sure you want to delete this channel?')">Delete</button>
</form>
</td>
</tr>
{{else}}
<tr>
<td colspan="7" class="text-center">No channels found</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -0,0 +1,93 @@
{{define "content"}}
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Channel Plugins</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>ID</th>
<th>Channel</th>
<th>Plugin</th>
<th>Enabled</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .Channels}}
{{range $pluginID, $channelPlugin := .Plugins}}
<tr>
<td>{{$channelPlugin.ID}}</td>
<td><a href="/admin/channels/{{.ID}}">{{.ChannelName}}</a></td>
<td>{{$pluginID}}</td>
<td>
{{if $channelPlugin.Enabled}}
<span class="badge bg-success">Enabled</span>
{{else}}
<span class="badge bg-danger">Disabled</span>
{{end}}
</td>
<td>
<form method="post" action="/admin/channelplugins/{{$channelPlugin.ID}}" class="d-inline">
<input type="hidden" name="enabled" value="{{if $channelPlugin.Enabled}}false{{else}}true{{end}}">
<button type="submit" class="btn btn-sm {{if $channelPlugin.Enabled}}btn-danger{{else}}btn-success{{end}}">
{{if $channelPlugin.Enabled}}Disable{{else}}Enable{{end}}
</button>
</form>
<form method="post" action="/admin/channelplugins/{{$channelPlugin.ID}}/delete" class="d-inline">
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Are you sure you want to remove this plugin?')">Remove</button>
</form>
</td>
</tr>
{{end}}
{{else}}
<tr>
<td colspan="5" class="text-center">No channel plugins found</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<hr>
<h4>Add Plugin to Channel</h4>
<form method="post" action="/admin/channelplugins">
<div class="mb-3">
<label class="form-label">Channel</label>
<select name="channel_id" class="form-select" required>
<option value="">Select a channel</option>
{{range .Channels}}
<option value="{{.ID}}">{{.ChannelName}} ({{.Platform}}:{{.PlatformChannelID}})</option>
{{end}}
</select>
</div>
<div class="mb-3">
<label class="form-label">Plugin</label>
<select name="plugin_id" class="form-select" required>
<option value="">Select a plugin</option>
{{range $id, $plugin := .Plugins}}
<option value="{{$id}}">{{$plugin.GetName}} ({{$id}})</option>
{{end}}
</select>
</div>
<div class="mb-3">
<label class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="enabled" value="y">
<span class="form-check-label">Enable plugin</span>
</label>
</div>
<div class="form-footer">
<button type="submit" class="btn btn-primary">Add Plugin</button>
</div>
</form>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -0,0 +1,15 @@
{{define "content"}}
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">ButterRobot Admin</h3>
</div>
<div class="card-body">
<p>Welcome to the ButterRobot admin interface.</p>
<p>Use the navigation above to manage channels and plugins.</p>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -0,0 +1,26 @@
{{define "content"}}
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="card-title">Login</h3>
</div>
<div class="card-body">
<form method="post" action="/admin/login">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" name="username" class="form-control" placeholder="Enter username" required>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" name="password" class="form-control" placeholder="Password" required>
</div>
<div class="form-footer">
<button type="submit" class="btn btn-primary">Sign in</button>
</div>
</form>
</div>
</div>
</div>
</div>
{{end}}

View file

@ -0,0 +1,45 @@
{{define "content"}}
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Available Plugins</h3>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Help</th>
<th>Requires Config</th>
</tr>
</thead>
<tbody>
{{range $id, $plugin := .Plugins}}
<tr>
<td>{{$id}}</td>
<td>{{$plugin.GetName}}</td>
<td>{{$plugin.GetHelp}}</td>
<td>
{{if $plugin.RequiresConfig}}
<span class="badge bg-yellow">Yes</span>
{{else}}
<span class="badge bg-green">No</span>
{{end}}
</td>
</tr>
{{else}}
<tr>
<td colspan="4" class="text-center">No plugins found</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{end}}