mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-06-13 23:36:45 +03:00
fix(admin): use basePath for API fetches when urlPrefix is set (#9197)
* fix(admin): use basePath for API fetches when urlPrefix is set * fix(admin): drop duplicate iam-utils script on Groups page * fix(admin): route topics page fetches through basePath The Topics page missed two fetch() calls that still used root-relative URLs, so create-topic and view-details still broke when -urlPrefix was set. --------- Co-authored-by: Maksim Babkou <maksim.babkou@innovatrics.com> Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
@@ -231,7 +231,6 @@ templ Groups(data dash.GroupsPageData) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/iam-utils.js"></script>
|
||||
<script>
|
||||
// Groups page JavaScript
|
||||
let currentGroupName = '';
|
||||
@@ -243,7 +242,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/api/groups', {
|
||||
const response = await fetch(basePath('/api/groups'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name: name })
|
||||
@@ -269,7 +268,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
|
||||
async function refreshGroupDetails(requestedName) {
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(requestedName));
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(requestedName)));
|
||||
if (!response.ok) throw new Error('Failed to fetch group');
|
||||
if (requestedName !== currentGroupName) return; // stale response
|
||||
const group = await response.json();
|
||||
@@ -339,7 +338,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
const username = document.getElementById('addMemberSelect').value;
|
||||
if (!username) return;
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(currentGroupName) + '/members', {
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(currentGroupName) + '/members'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: username })
|
||||
@@ -358,7 +357,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
|
||||
async function removeMember(username) {
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(currentGroupName) + '/members/' + encodeURIComponent(username), {
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(currentGroupName) + '/members/' + encodeURIComponent(username)), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (response.ok) {
|
||||
@@ -377,7 +376,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
const policyName = document.getElementById('attachPolicySelect').value;
|
||||
if (!policyName) return;
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(currentGroupName) + '/policies', {
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(currentGroupName) + '/policies'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ policy_name: policyName })
|
||||
@@ -396,7 +395,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
|
||||
async function detachPolicy(policyName) {
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(currentGroupName) + '/policies/' + encodeURIComponent(policyName), {
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(currentGroupName) + '/policies/' + encodeURIComponent(policyName)), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (response.ok) {
|
||||
@@ -414,7 +413,7 @@ templ Groups(data dash.GroupsPageData) {
|
||||
async function toggleGroupStatus() {
|
||||
const enabled = document.getElementById('groupEnabledSwitch').checked;
|
||||
try {
|
||||
const response = await fetch('/api/groups/' + encodeURIComponent(currentGroupName) + '/status', {
|
||||
const response = await fetch(basePath('/api/groups/' + encodeURIComponent(currentGroupName) + '/status'), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ enabled: enabled })
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -504,6 +504,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
const STATUS_ACTIVE = 'Active';
|
||||
const STATUS_INACTIVE = 'Inactive';
|
||||
|
||||
// Use basePath() from admin.js for -urlPrefix; it is loaded in the layout footer before DOMContentLoaded fires.
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Event delegation for user action buttons
|
||||
@@ -591,7 +592,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
// Load buckets
|
||||
async function loadBuckets() {
|
||||
try {
|
||||
const response = await fetch('/api/s3/buckets');
|
||||
const response = await fetch(basePath('/api/s3/buckets'));
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
availableBuckets = (data.buckets || []).map(bucket => ({ name: bucket.name, type: 's3' }));
|
||||
@@ -605,7 +606,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
availableBuckets = [];
|
||||
}
|
||||
try {
|
||||
const response = await fetch('/api/s3tables/buckets');
|
||||
const response = await fetch(basePath('/api/s3tables/buckets'));
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const tableBuckets = (data.buckets || data.tableBuckets || []).map(bucket => ({ name: bucket.name, type: 's3tables' }));
|
||||
@@ -623,7 +624,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
// Load policies
|
||||
async function loadPolicies() {
|
||||
try {
|
||||
const response = await fetch('/api/object-store/policies');
|
||||
const response = await fetch(basePath('/api/object-store/policies'));
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const policies = data.policies || [];
|
||||
@@ -910,7 +911,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
async function showUserDetails(username) {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`);
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`));
|
||||
if (response.ok) {
|
||||
const user = await response.json();
|
||||
document.getElementById('userDetailsContent').innerHTML = createUserDetailsContent(user);
|
||||
@@ -929,7 +930,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
async function editUser(username) {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`);
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`));
|
||||
if (response.ok) {
|
||||
const user = await response.json();
|
||||
|
||||
@@ -1008,7 +1009,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
async function manageAccessKeys(username) {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`);
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`));
|
||||
if (response.ok) {
|
||||
const user = await response.json();
|
||||
document.getElementById('accessKeysUsername').textContent = username;
|
||||
@@ -1029,7 +1030,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
showDeleteConfirm(username, async function() {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`, {
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
@@ -1074,7 +1075,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/users', {
|
||||
const response = await fetch(basePath('/api/users'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -1118,13 +1119,13 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
|
||||
try {
|
||||
// Fetch all groups
|
||||
const groupsResp = await fetch('/api/groups');
|
||||
const groupsResp = await fetch(basePath('/api/groups'));
|
||||
if (!groupsResp.ok) return;
|
||||
const groupsData = await groupsResp.json();
|
||||
const allGroups = groupsData.groups || [];
|
||||
|
||||
// Fetch user details to get current groups
|
||||
const userResp = await fetch(`/api/users/${encodeURIComponent(username)}`);
|
||||
const userResp = await fetch(basePath(`/api/users/${encodeURIComponent(username)}`));
|
||||
if (!userResp.ok) return;
|
||||
const user = await userResp.json();
|
||||
const userGroups = user.groups || [];
|
||||
@@ -1172,7 +1173,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
if (!groupName) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/groups/${encodeURIComponent(groupName)}/members`, {
|
||||
const response = await fetch(basePath(`/api/groups/${encodeURIComponent(groupName)}/members`), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: username })
|
||||
@@ -1192,7 +1193,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
async function removeUserFromGroupInEdit(groupName) {
|
||||
const username = document.getElementById('editUsername').value;
|
||||
try {
|
||||
const response = await fetch(`/api/groups/${encodeURIComponent(groupName)}/members/${encodeURIComponent(username)}`, {
|
||||
const response = await fetch(basePath(`/api/groups/${encodeURIComponent(groupName)}/members/${encodeURIComponent(username)}`), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
if (response.ok) {
|
||||
@@ -1237,7 +1238,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`, {
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -1374,7 +1375,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
async function refreshAccessKeysList(username) {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}`);
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}`));
|
||||
if (response.ok) {
|
||||
const user = await response.json();
|
||||
document.getElementById('accessKeysContent').innerHTML = createAccessKeysContent(user);
|
||||
@@ -1387,7 +1388,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
// Update access key status
|
||||
async function updateAccessKeyStatus(username, accessKey, status) {
|
||||
try {
|
||||
const response = await fetch(`/api/users/${encodeURIComponent(username)}/access-keys/${encodeURIComponent(accessKey)}/status`, {
|
||||
const response = await fetch(basePath(`/api/users/${encodeURIComponent(username)}/access-keys/${encodeURIComponent(accessKey)}/status`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -1473,7 +1474,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const response = await fetch(`/api/users/${encodedUsername}/access-keys`, {
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}/access-keys`), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -1514,7 +1515,7 @@ templ ObjectStoreUsers(data dash.ObjectStoreUsersData) {
|
||||
try {
|
||||
const encodedUsername = encodeURIComponent(username);
|
||||
const encodedAccessKey = encodeURIComponent(accessKey);
|
||||
const response = await fetch(`/api/users/${encodedUsername}/access-keys/${encodedAccessKey}`, {
|
||||
const response = await fetch(basePath(`/api/users/${encodedUsername}/access-keys/${encodedAccessKey}`), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -368,7 +368,7 @@ templ Policies(data dash.PoliciesData) {
|
||||
document: policyDocument
|
||||
};
|
||||
|
||||
fetch('/api/object-store/policies', {
|
||||
fetch(basePath('/api/object-store/policies'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -408,7 +408,7 @@ templ Policies(data dash.PoliciesData) {
|
||||
`;
|
||||
|
||||
// Fetch policy data
|
||||
fetch('/api/object-store/policies/' + encodeURIComponent(policyName))
|
||||
fetch(basePath('/api/object-store/policies/' + encodeURIComponent(policyName)))
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Policy not found');
|
||||
@@ -494,7 +494,7 @@ templ Policies(data dash.PoliciesData) {
|
||||
document.getElementById('editPolicyDocument').value = 'Loading...';
|
||||
|
||||
// Fetch policy data
|
||||
fetch('/api/object-store/policies/' + encodeURIComponent(policyName))
|
||||
fetch(basePath('/api/object-store/policies/' + encodeURIComponent(policyName)))
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Policy not found');
|
||||
@@ -534,7 +534,7 @@ templ Policies(data dash.PoliciesData) {
|
||||
document: policyDocument
|
||||
};
|
||||
|
||||
fetch('/api/object-store/policies/' + encodeURIComponent(policyName), {
|
||||
fetch(basePath('/api/object-store/policies/' + encodeURIComponent(policyName)), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -636,7 +636,7 @@ templ Policies(data dash.PoliciesData) {
|
||||
|
||||
function deletePolicy(policyName) {
|
||||
showDeleteConfirm(policyName, function() {
|
||||
fetch('/api/object-store/policies/' + encodeURIComponent(policyName), {
|
||||
fetch(basePath('/api/object-store/policies/' + encodeURIComponent(policyName)), {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(response => response.json())
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -712,7 +712,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
// Only fetch if not already populated
|
||||
if (ownerSelect.options.length <= 1) {
|
||||
try {
|
||||
const response = await fetch('/api/users');
|
||||
const response = await fetch(basePath('/api/users'));
|
||||
const data = await response.json();
|
||||
const users = data.users || [];
|
||||
|
||||
@@ -756,7 +756,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/s3/buckets', {
|
||||
fetch(basePath('/api/s3/buckets'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -846,7 +846,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
quota_enabled: enabled
|
||||
};
|
||||
|
||||
fetch(`/api/s3/buckets/${bucketName}/quota`, {
|
||||
fetch(basePath(`/api/s3/buckets/${bucketName}/quota`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -897,7 +897,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
// Fetch users if not cached
|
||||
try {
|
||||
if (!cachedUsers) {
|
||||
const response = await fetch('/api/users');
|
||||
const response = await fetch(basePath('/api/users'));
|
||||
const data = await response.json();
|
||||
cachedUsers = data.users || [];
|
||||
}
|
||||
@@ -941,7 +941,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
const owner = document.getElementById('bucketOwnerSelect').value;
|
||||
const data = { owner: owner };
|
||||
|
||||
fetch(`/api/s3/buckets/${bucketName}/owner`, {
|
||||
fetch(basePath(`/api/s3/buckets/${bucketName}/owner`), {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -988,7 +988,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
detailsModalInstance.show();
|
||||
|
||||
// Fetch bucket details
|
||||
fetch('/api/s3/buckets/' + bucketName)
|
||||
fetch(basePath('/api/s3/buckets/' + bucketName))
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
@@ -1017,7 +1017,7 @@ templ S3Buckets(data dash.S3BucketsData) {
|
||||
const bucketName = document.getElementById('deleteBucketModal').dataset.bucketName;
|
||||
if (!bucketName) return;
|
||||
|
||||
fetch(`/api/s3/buckets/${bucketName}`, {
|
||||
fetch(basePath(`/api/s3/buckets/${bucketName}`), {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(response => response.json())
|
||||
@@ -1172,7 +1172,7 @@ function displayBucketDetails(data) {
|
||||
}
|
||||
|
||||
// Fetch all buckets from the API (not just the current page)
|
||||
fetch('/api/s3/buckets')
|
||||
fetch(basePath('/api/s3/buckets'))
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -336,7 +336,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) {
|
||||
async function showSADetails(id) {
|
||||
try {
|
||||
const encodedId = encodeURIComponent(id);
|
||||
const response = await fetch(`/api/service-accounts/${encodedId}`);
|
||||
const response = await fetch(basePath(`/api/service-accounts/${encodedId}`));
|
||||
if (response.ok) {
|
||||
const sa = await response.json();
|
||||
document.getElementById('saDetailsContent').innerHTML = createSADetailsContent(sa);
|
||||
@@ -355,7 +355,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) {
|
||||
const newStatus = currentStatus === 'Active' ? 'Inactive' : 'Active';
|
||||
try {
|
||||
const encodedId = encodeURIComponent(id);
|
||||
const response = await fetch(`/api/service-accounts/${encodedId}`, {
|
||||
const response = await fetch(basePath(`/api/service-accounts/${encodedId}`), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ status: newStatus })
|
||||
@@ -378,7 +378,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) {
|
||||
showDeleteConfirm(id, async function() {
|
||||
try {
|
||||
const encodedId = encodeURIComponent(id);
|
||||
const response = await fetch(`/api/service-accounts/${encodedId}`, {
|
||||
const response = await fetch(basePath(`/api/service-accounts/${encodedId}`), {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
@@ -427,7 +427,7 @@ templ ServiceAccounts(data dash.ServiceAccountsData) {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/service-accounts', {
|
||||
const response = await fetch(basePath('/api/service-accounts'), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(saData)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -273,7 +273,7 @@ templ Topics(data dash.TopicsData) {
|
||||
};
|
||||
|
||||
// Create the topic
|
||||
fetch('/api/mq/topics/create', {
|
||||
fetch(basePath('/api/mq/topics/create'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@@ -354,7 +354,7 @@ templ Topics(data dash.TopicsData) {
|
||||
var detailsRow = document.getElementById('details-' + topicName.replace(/\./g, '_'));
|
||||
var contentDiv = detailsRow.querySelector('.topic-details-content');
|
||||
|
||||
fetch('/admin/topics/' + encodeURIComponent(topicName) + '/details')
|
||||
fetch(basePath('/admin/topics/' + encodeURIComponent(topicName) + '/details'))
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var html = '<div class="row">';
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user