mirror of
https://github.com/versity/versitygw.git
synced 2026-03-27 01:45:02 +00:00
Merge pull request #1942 from versity/ben/webui-template
feat: remove /api/gateways webserver endpoint
This commit is contained in:
55
webui/web/assets/css/tailwind-config.js
Normal file
55
webui/web/assets/css/tailwind-config.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2026 Versity Software
|
||||
* This file is licensed under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// Shared Tailwind CSS configuration for all pages.
|
||||
// Edit this file to change the global color palette, typography, etc.
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#002A7A',
|
||||
50: '#E6EBF4',
|
||||
100: '#B3C2E0',
|
||||
200: '#809ACC',
|
||||
300: '#4D71B8',
|
||||
400: '#264DA3',
|
||||
500: '#002A7A',
|
||||
600: '#002468',
|
||||
700: '#001D56',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: '#0076CD',
|
||||
50: '#E6F3FA',
|
||||
100: '#B3DCF2',
|
||||
500: '#0076CD',
|
||||
600: '#0065AF',
|
||||
},
|
||||
charcoal: {
|
||||
DEFAULT: '#191B2A',
|
||||
300: '#757884',
|
||||
400: '#565968',
|
||||
},
|
||||
surface: {
|
||||
DEFAULT: '#F3F8FC',
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Roboto', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
194
webui/web/assets/css/theme.css
Normal file
194
webui/web/assets/css/theme.css
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2026 Versity Software
|
||||
* This file is licensed under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* theme.css — Global shared styles for all pages.
|
||||
* Page-specific styles remain inline in their respective HTML files.
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Sidebar Navigation */
|
||||
.nav-item {
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.nav-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.nav-item.active {
|
||||
background: rgba(0, 118, 205, 0.2);
|
||||
border-left: 4px solid #0076CD;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal-backdrop {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Custom Dropdown */
|
||||
.custom-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background: white;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
max-height: 12rem;
|
||||
overflow: auto;
|
||||
}
|
||||
.custom-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
.custom-dropdown-item {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
color: #191B2A;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
.custom-dropdown-item:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
.custom-dropdown-item.selected {
|
||||
background-color: rgba(0, 118, 205, 0.1);
|
||||
color: #0076CD;
|
||||
}
|
||||
/* Dropup variant — opens upward */
|
||||
.custom-dropdown.dropup {
|
||||
bottom: 100%;
|
||||
top: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Toggle Switch */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 28px;
|
||||
}
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #e5e7eb;
|
||||
transition: 0.3s;
|
||||
border-radius: 28px;
|
||||
}
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
input:checked + .toggle-slider {
|
||||
background-color: #0076CD;
|
||||
}
|
||||
input:checked + .toggle-slider:before {
|
||||
transform: translateX(32px);
|
||||
}
|
||||
.toggle-label {
|
||||
font-size: 0.875rem;
|
||||
color: #565968;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Login Page */
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #808080;
|
||||
}
|
||||
.input-with-icon {
|
||||
padding-left: 44px;
|
||||
}
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #808080;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
}
|
||||
.password-toggle:hover {
|
||||
color: #002A7A;
|
||||
}
|
||||
.advanced-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.75rem 0;
|
||||
margin: 0.5rem 0;
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.advanced-toggle:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.advanced-toggle-carat {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.advanced-toggle.expanded .advanced-toggle-carat {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.advanced-toggle-label {
|
||||
font-weight: 500;
|
||||
color: #565968;
|
||||
cursor: pointer;
|
||||
}
|
||||
.advanced-options {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
.advanced-options.show {
|
||||
max-height: 500px;
|
||||
}
|
||||
@@ -21,67 +21,10 @@ under the License.
|
||||
<title>VersityGW Admin - Buckets</title>
|
||||
<script src="assets/js/crypto-js.min.js"></script>
|
||||
<script src="assets/css/tailwind.js"></script>
|
||||
<script src="assets/css/tailwind-config.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css">
|
||||
<link rel="stylesheet" href="assets/css/theme.css">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon.png">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: { DEFAULT: '#002A7A', 50: '#E6EBF4', 500: '#002A7A', 600: '#002468' },
|
||||
accent: { DEFAULT: '#0076CD', 50: '#E6F3FA', 500: '#0076CD', 600: '#0065AF' },
|
||||
charcoal: { DEFAULT: '#191B2A', 300: '#757884', 400: '#565968' },
|
||||
surface: { DEFAULT: '#F3F8FC' }
|
||||
},
|
||||
fontFamily: { sans: ['Roboto', 'system-ui', 'sans-serif'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: 'Roboto', system-ui, sans-serif; }
|
||||
.nav-item { transition: all 0.15s ease; }
|
||||
.nav-item:hover { background: rgba(255,255,255,0.1); }
|
||||
.nav-item.active { background: rgba(0, 118, 205, 0.2); border-left: 4px solid #0076CD; }
|
||||
.modal-backdrop { background: rgba(0,0,0,0.5); backdrop-filter: blur(4px); }
|
||||
/* Custom dropdown styles */
|
||||
.custom-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background: white;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
max-height: 12rem;
|
||||
overflow: auto;
|
||||
}
|
||||
.custom-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
.custom-dropdown-item {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
color: #191B2A;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
.custom-dropdown-item:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
.custom-dropdown-item.selected {
|
||||
background-color: rgba(0, 118, 205, 0.1);
|
||||
color: #0076CD;
|
||||
}
|
||||
/* Dropup variant - opens upward */
|
||||
.custom-dropdown.dropup {
|
||||
bottom: 100%;
|
||||
top: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen bg-surface">
|
||||
<script src="js/api.js"></script>
|
||||
|
||||
@@ -21,29 +21,10 @@ under the License.
|
||||
<title>VersityGW Admin - Dashboard</title>
|
||||
<script src="assets/js/crypto-js.min.js"></script>
|
||||
<script src="assets/css/tailwind.js"></script>
|
||||
<script src="assets/css/tailwind-config.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css">
|
||||
<link rel="stylesheet" href="assets/css/theme.css">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon.png">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: { DEFAULT: '#002A7A', 50: '#E6EBF4', 500: '#002A7A', 600: '#002468' },
|
||||
accent: { DEFAULT: '#0076CD', 50: '#E6F3FA', 500: '#0076CD', 600: '#0065AF' },
|
||||
charcoal: { DEFAULT: '#191B2A', 300: '#757884', 400: '#565968' },
|
||||
surface: { DEFAULT: '#F3F8FC' }
|
||||
},
|
||||
fontFamily: { sans: ['Roboto', 'system-ui', 'sans-serif'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: 'Roboto', system-ui, sans-serif; }
|
||||
.nav-item { transition: all 0.15s ease; }
|
||||
.nav-item:hover { background: rgba(255,255,255,0.1); }
|
||||
.nav-item.active { background: rgba(0, 118, 205, 0.2); border-left: 4px solid #0076CD; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen bg-surface">
|
||||
<script src="js/api.js"></script>
|
||||
|
||||
@@ -21,28 +21,11 @@ under the License.
|
||||
<title>VersityGW - Explorer</title>
|
||||
<script src="assets/js/crypto-js.min.js"></script>
|
||||
<script src="assets/css/tailwind.js"></script>
|
||||
<script src="assets/css/tailwind-config.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css">
|
||||
<link rel="stylesheet" href="assets/css/theme.css">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon.png">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: { DEFAULT: '#002A7A', 50: '#E6EBF4', 500: '#002A7A', 600: '#002468' },
|
||||
accent: { DEFAULT: '#0076CD', 50: '#E6F3FA', 500: '#0076CD', 600: '#0065AF' },
|
||||
charcoal: { DEFAULT: '#191B2A', 300: '#757884', 400: '#565968' },
|
||||
surface: { DEFAULT: '#F3F8FC' }
|
||||
},
|
||||
fontFamily: { sans: ['Roboto', 'system-ui', 'sans-serif'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: 'Roboto', system-ui, sans-serif; }
|
||||
.nav-item { transition: all 0.15s ease; }
|
||||
.nav-item:hover { background: rgba(255,255,255,0.1); }
|
||||
.nav-item.active { background: rgba(0, 118, 205, 0.2); border-left: 4px solid #0076CD; }
|
||||
.drop-zone-active { background: rgba(0, 118, 205, 0.1); border-color: #0076CD; }
|
||||
.file-row:hover { background: #f9fafb; }
|
||||
.file-row.selected { background: #e6f3fa; }
|
||||
|
||||
@@ -21,191 +21,10 @@ under the License.
|
||||
<title>VersityGW Admin - Login</title>
|
||||
<script src="assets/js/crypto-js.min.js"></script>
|
||||
<script src="assets/css/tailwind.js"></script>
|
||||
<script src="assets/css/tailwind-config.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css">
|
||||
<link rel="stylesheet" href="assets/css/theme.css">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon.png">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
DEFAULT: '#002A7A',
|
||||
50: '#E6EBF4',
|
||||
100: '#B3C2E0',
|
||||
200: '#809ACC',
|
||||
300: '#4D71B8',
|
||||
400: '#264DA3',
|
||||
500: '#002A7A',
|
||||
600: '#002468',
|
||||
700: '#001D56',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: '#0076CD',
|
||||
50: '#E6F3FA',
|
||||
100: '#B3DCF2',
|
||||
500: '#0076CD',
|
||||
600: '#0065AF',
|
||||
},
|
||||
charcoal: {
|
||||
DEFAULT: '#191B2A',
|
||||
300: '#757884',
|
||||
400: '#565968',
|
||||
},
|
||||
surface: {
|
||||
DEFAULT: '#F3F8FC',
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Roboto', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: 'Roboto', system-ui, sans-serif; }
|
||||
.input-icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #808080;
|
||||
}
|
||||
.input-with-icon {
|
||||
padding-left: 44px;
|
||||
}
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #808080;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
}
|
||||
.password-toggle:hover {
|
||||
color: #002A7A;
|
||||
}
|
||||
.advanced-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
padding: 0.75rem 0;
|
||||
margin: 0.5rem 0;
|
||||
background: none;
|
||||
border: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
.advanced-toggle:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.advanced-toggle-carat {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.advanced-toggle.expanded .advanced-toggle-carat {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.advanced-toggle-label {
|
||||
font-weight: 500;
|
||||
color: #565968;
|
||||
cursor: pointer;
|
||||
}
|
||||
.advanced-options {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
.advanced-options.show {
|
||||
max-height: 500px;
|
||||
}
|
||||
/* Custom dropdown styles */
|
||||
.custom-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background: white;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
max-height: 12rem;
|
||||
overflow: auto;
|
||||
}
|
||||
.custom-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
.custom-dropdown-item {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
color: #191B2A;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
.custom-dropdown-item:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
.custom-dropdown-item.selected {
|
||||
background-color: rgba(0, 118, 205, 0.1);
|
||||
color: #0076CD;
|
||||
}
|
||||
/* Toggle Switch Styles */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 28px;
|
||||
}
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #e5e7eb;
|
||||
transition: 0.3s;
|
||||
border-radius: 28px;
|
||||
}
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
input:checked + .toggle-slider {
|
||||
background-color: #0076CD;
|
||||
}
|
||||
input:checked + .toggle-slider:before {
|
||||
transform: translateX(32px);
|
||||
}
|
||||
.toggle-label {
|
||||
font-size: 0.875rem;
|
||||
color: #565968;
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen bg-gradient-to-br from-surface to-white flex items-center justify-center p-4">
|
||||
<script src="js/api.js"></script>
|
||||
@@ -405,7 +224,7 @@ under the License.
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Configured Gateways (from vgwmgr CLI)
|
||||
// Configured Gateways
|
||||
// ============================================
|
||||
let configuredGateways = [];
|
||||
let configuredAdminGateways = [];
|
||||
@@ -434,24 +253,18 @@ under the License.
|
||||
return out;
|
||||
}
|
||||
|
||||
async function loadConfiguredGateways() {
|
||||
try {
|
||||
const res = await fetch('/api/gateways', { cache: 'no-store' });
|
||||
if (!res.ok) return { gateways: [], adminGateways: [], defaultRegion: null };
|
||||
const data = await res.json();
|
||||
if (!data || !Array.isArray(data.gateways)) return { gateways: [], adminGateways: [], defaultRegion: null };
|
||||
return {
|
||||
gateways: data.gateways,
|
||||
adminGateways: data.adminGateways || data.gateways || [],
|
||||
defaultRegion: normalizeRegion(typeof data.defaultRegion === 'string' ? data.defaultRegion : null),
|
||||
};
|
||||
} catch (e) {
|
||||
return { gateways: [], adminGateways: [], defaultRegion: null };
|
||||
}
|
||||
function loadConfiguredGateways() {
|
||||
const cfg = window.__VGWCONFIG__ || {};
|
||||
if (!Array.isArray(cfg.gateways)) return { gateways: [], adminGateways: [], defaultRegion: null };
|
||||
return {
|
||||
gateways: cfg.gateways,
|
||||
adminGateways: cfg.adminGateways || cfg.gateways || [],
|
||||
defaultRegion: normalizeRegion(typeof cfg.defaultRegion === 'string' ? cfg.defaultRegion : null),
|
||||
};
|
||||
}
|
||||
|
||||
async function initConfiguredGateways() {
|
||||
const cfg = await loadConfiguredGateways();
|
||||
function initConfiguredGateways() {
|
||||
const cfg = loadConfiguredGateways();
|
||||
configuredGateways = uniqNonEmpty(cfg.gateways);
|
||||
configuredAdminGateways = uniqNonEmpty(cfg.adminGateways);
|
||||
configuredDefaultRegion = cfg.defaultRegion;
|
||||
|
||||
@@ -291,15 +291,6 @@ class VersityAPI {
|
||||
// User Context Methods (ROOT user detection)
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Detect ROOT user and get accessible gateways
|
||||
* Calls /api/detect-root endpoint to check if credentials match ROOT config
|
||||
* @param {string} accessKey - Access key to check
|
||||
* @param {string} secretKey - Secret key to check
|
||||
* @returns {Object} - { userType: 'root'|'user', matchingGateways: [...] }
|
||||
*/
|
||||
// detectRootUser removed - single gateway mode
|
||||
|
||||
/**
|
||||
* Store user type and accessible gateways in session
|
||||
* @param {string} userType - 'root' | 'admin' | 'user'
|
||||
@@ -318,26 +309,6 @@ class VersityAPI {
|
||||
this._userType = sessionStorage.getItem('vgw_user_type') || 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is ROOT user (has ROOT credentials matching gateway configs)
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// isRootUser removed
|
||||
|
||||
/**
|
||||
* Get list of gateways accessible to current user
|
||||
* Only populated for ROOT users
|
||||
* @returns {Array} - Array of { name, port, endpoint, region, status }
|
||||
*/
|
||||
// getAccessibleGateways removed
|
||||
|
||||
/**
|
||||
* Check if authenticated
|
||||
*/
|
||||
isAuthenticated() {
|
||||
return this.credentials !== null && this.s3Endpoint !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has admin privileges
|
||||
*/
|
||||
|
||||
1
webui/web/js/config.js.tmpl
Normal file
1
webui/web/js/config.js.tmpl
Normal file
@@ -0,0 +1 @@
|
||||
window.__VGWCONFIG__ = {{.ConfigJSON}};
|
||||
@@ -21,60 +21,10 @@ under the License.
|
||||
<title>VersityGW Admin - Users</title>
|
||||
<script src="assets/js/crypto-js.min.js"></script>
|
||||
<script src="assets/css/tailwind.js"></script>
|
||||
<script src="assets/css/tailwind-config.js"></script>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css">
|
||||
<link rel="stylesheet" href="assets/css/theme.css">
|
||||
<link rel="icon" type="image/png" href="assets/images/favicon.png">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: { DEFAULT: '#002A7A', 50: '#E6EBF4', 500: '#002A7A', 600: '#002468' },
|
||||
accent: { DEFAULT: '#0076CD', 50: '#E6F3FA', 500: '#0076CD', 600: '#0065AF' },
|
||||
charcoal: { DEFAULT: '#191B2A', 300: '#757884', 400: '#565968' },
|
||||
surface: { DEFAULT: '#F3F8FC' }
|
||||
},
|
||||
fontFamily: { sans: ['Roboto', 'system-ui', 'sans-serif'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { font-family: 'Roboto', system-ui, sans-serif; }
|
||||
.nav-item { transition: all 0.15s ease; }
|
||||
.nav-item:hover { background: rgba(255,255,255,0.1); }
|
||||
.nav-item.active { background: rgba(0, 118, 205, 0.2); border-left: 4px solid #0076CD; }
|
||||
.modal-backdrop { background: rgba(0,0,0,0.5); backdrop-filter: blur(4px); }
|
||||
/* Custom dropdown styles */
|
||||
.custom-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background: white;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||
max-height: 12rem;
|
||||
overflow: auto;
|
||||
}
|
||||
.custom-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
.custom-dropdown-item {
|
||||
padding: 0.75rem 1rem;
|
||||
cursor: pointer;
|
||||
color: #191B2A;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
.custom-dropdown-item:hover {
|
||||
background-color: #f9fafb;
|
||||
}
|
||||
.custom-dropdown-item.selected {
|
||||
background-color: rgba(0, 118, 205, 0.1);
|
||||
color: #0076CD;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-screen bg-surface">
|
||||
<script src="js/api.js"></script>
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
||||
@@ -96,32 +98,48 @@ func (s *Server) setupMiddleware() {
|
||||
|
||||
// setupRoutes configures all routes
|
||||
func (s *Server) setupRoutes() {
|
||||
// API endpoint to get configured gateways
|
||||
s.app.Get("/api/gateways", s.handleGetGateways)
|
||||
// Serve index.html
|
||||
s.app.Get("/", s.handleIndexHTML)
|
||||
s.app.Get("/index.html", s.handleIndexHTML)
|
||||
|
||||
// Serve embedded static files from web/
|
||||
s.app.Use("/", filesystem.New(filesystem.Config{
|
||||
Root: http.FS(webFS),
|
||||
PathPrefix: "web",
|
||||
Index: "index.html",
|
||||
NotFoundFile: "index.html", // SPA fallback
|
||||
Browse: false,
|
||||
Root: http.FS(webFS),
|
||||
PathPrefix: "web",
|
||||
Browse: false,
|
||||
}))
|
||||
}
|
||||
|
||||
// handleGetGateways returns the configured gateway URLs (both S3 and Admin)
|
||||
func (s *Server) handleGetGateways(c *fiber.Ctx) error {
|
||||
// handleIndexHTML serves index.html with server config injected as an inline script.
|
||||
func (s *Server) handleIndexHTML(c *fiber.Ctx) error {
|
||||
data, err := webFiles.ReadFile("web/index.html")
|
||||
if err != nil {
|
||||
return fiber.ErrInternalServerError
|
||||
}
|
||||
|
||||
adminGateways := s.config.AdminGateways
|
||||
if len(adminGateways) == 0 {
|
||||
// Fallback to S3 gateways if admin gateways not configured
|
||||
adminGateways = s.config.Gateways
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
configJSON, err := json.Marshal(map[string]interface{}{
|
||||
"gateways": s.config.Gateways,
|
||||
"adminGateways": adminGateways,
|
||||
"defaultRegion": s.config.Region,
|
||||
})
|
||||
if err != nil {
|
||||
return fiber.ErrInternalServerError
|
||||
}
|
||||
|
||||
html := strings.Replace(
|
||||
string(data),
|
||||
"</head>",
|
||||
"<script>window.__VGWCONFIG__ = "+string(configJSON)+";</script></head>",
|
||||
1,
|
||||
)
|
||||
|
||||
c.Set("Content-Type", "text/html; charset=utf-8")
|
||||
return c.SendString(html)
|
||||
}
|
||||
|
||||
// ServeMultiPort creates listeners for multiple address specifications and serves
|
||||
|
||||
Reference in New Issue
Block a user