Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ jobs:
-
name: Install hanami
run: |
helm install --set docker.tag="${{ env.BRANCH_NAME }}" --set user.id=asdf --set user.name="test user" --set user.passphrase="asdfasdf" --set token.data="this is a test-token" --set api.domain=local-hanami ainari /tmp/ainari_helm_build_result/ainari-$HELM_VERSION.tgz
helm install --set docker.tag="${{ env.BRANCH_NAME }}" --set miko.user.id=asdf --set miko.user.name="test user" --set miko.user.passphrase="asdfasdf" --set miko.token.data="this is a test-token" ainari /tmp/ainari_helm_build_result/ainari-$HELM_VERSION.tgz
-
name: Sleep for 120 seconds
uses: jakejarvis/wait-action@919fc193e07906705e5b7a50f90ea9e74d20b2b0
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,23 @@
- image pull-policy can now be changed by the helm-values
- support other namespaces thand "default"
- support for multiple sakura instances
- dashboard:
- added error-popups
- validation of input-fields and error-message for invalid inputs
- added cluster-list without actions to overview-page
- automatic logout in case the token is expired

### Changed

- changed the image-build process for the new vagrant-test-setup
- sakura and onsen are now statefulsets within the kubernetes setup
- dashboard
- updated imports
- messages when api-requests failed
- new progress-bar for task-progress with live update of the current task progress
- hide admin-section in case that the user is not an admin
- user-logo in the right upper corner now is always white with the starting letter of the user-id
- gauge-charts of the overview-page now has animation

### Fixed

Expand Down
120 changes: 91 additions & 29 deletions src/dashboard/app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<Topbar :username="username" @logout="handleLogout" />
<div class="main">
<Sidebar
:isAdmin="isAdmin"
@change-view="
({ view, id }) => {
currentView = view;
Expand All @@ -61,45 +62,53 @@
</div>
</template>
</div>

<div v-if="tokenExpireError" class="error-popup">
<button class="error-close-btn" @click="tokenExpireError = ''">
</button>
{{ tokenExpireError }}
</div>
</template>

<script setup lang="ts">
// Import necessary Vue composition API functions
import { ref, provide } from "vue";
import { ref, provide, onMounted, onUnmounted } from "vue";

// Import all the Vue components used in the application
import Sidebar from "./components/sidebar.vue";
import Topbar from "./components/topbar.vue";
import Login from "./components/login.vue";
import Overview from "./components/overview.vue";
import AdminUser from "./components/admin/user/user_overview.vue";
import AdminProject from "./components/admin/project/project_overview.vue";
import StorageCheckpoint from "./components/storage/checkpoint/checkpoint_overview.vue";
import StorageDataset from "./components/storage/dataset/dataset_overview.vue";
import WorkloadCluster from "./components/workload/cluster/cluster_overview.vue";
import WorkloadTask from "./components/workload/task/task_overview.vue";

// Import the authentication context module
// import context from "./auth_context";
import Sidebar from "@/components/sidebar.vue";
import Topbar from "@/components/topbar.vue";
import Login from "@/components/login.vue";
import Overview from "@/components/overview.vue";
import AdminUser from "@/components/admin/user/user_overview.vue";
import AdminProject from "@/components/admin/project/project_overview.vue";
import StorageCheckpoint from "@/components/storage/checkpoint/checkpoint_overview.vue";
import StorageDataset from "@/components/storage/dataset/dataset_overview.vue";
import WorkloadCluster from "@/components/workload/cluster/cluster_overview.vue";
import WorkloadTask from "@/components/workload/task/task_overview.vue";
import { getAuthContext } from "@/auth_context";

// Import all CSS styles for the application
import "./styles/base.css";
import "./styles/other.css";
import "./styles/card.css";
import "./styles/modal.css";
import "./styles/dropdown.css";
import "./styles/button.css";
import "./styles/table.css";
import "./styles/tab.css";
import "./styles/devider.css";
import "./styles/primevue_overrides.css";
import "@/styles/base.css";
import "@/styles/other.css";
import "@/styles/card.css";
import "@/styles/modal.css";
import "@/styles/dropdown.css";
import "@/styles/button.css";
import "@/styles/table.css";
import "@/styles/tab.css";
import "@/styles/devider.css";
import "@/styles/primevue_overrides.css";

// Reactive reference to track the current active view/component
// Defaults to "Overview" when the application loads
const currentView = ref("Overview");
const currentId = ref<string | null>(null);
const isLoggedIn = ref<boolean>(!!localStorage.getItem("ainari_authContext"));
const username = ref<string | null>(localStorage.getItem("username"));
const isAdmin = ref<boolean>(getAuthContext().is_admin === "true");
const tokenExpireError = ref<string>("");
var expiryInterval: number | undefined;

// Object containing all the available view components
// These will be dynamically rendered based on the currentView value
Expand All @@ -126,16 +135,26 @@ provide("icons", { acceptIcon, cancelIcon });
* Handles successful login by updating the application state
* @param newToken - The new authentication token received from the login process
* @param user - The username of the logged-in user
* @param is_admin - True, if the user is an admin
*/
function handleLoginSuccess(newToken: string, user: string) {
function handleLoginSuccess(
newToken: string,
user: string,
is_admin: string,
expire_timestamp: number,
) {
// Store the username in localStorage for persistence across page reloads
localStorage.setItem("username", user);

// Update the login state to true to disable the login-modal
isLoggedIn.value = true;
username.value = user;
isAdmin.value = is_admin === "true";
tokenExpireError.value = "";

// Log the current auth context (for debugging purposes)
// console.log("test: ", context.getAuthContext().value.token);
// Start watcher to check if the token is expired. To avoid conflicts
// the logout will be done 30 seonds before the token really expire.
startTokenExpiryWatcher(expire_timestamp - 30, handleLogout);

// Update the login state to true to disable the login-modal
}

/**
Expand All @@ -151,6 +170,49 @@ function handleLogout() {
isLoggedIn.value = false;
username.value = null;
}

function startTokenExpiryWatcher(
expireTimeUnix: number,
handleLogout: () => void,
) {
// Clear any previous watcher (important!)
if (expiryInterval) {
clearInterval(expiryInterval);
}

expiryInterval = window.setInterval(() => {
const nowUnix = Math.floor(Date.now() / 1000);

// Handle expired token
if (nowUnix >= expireTimeUnix) {
tokenExpireError.value = "Token expired. Please login again.";
clearInterval(expiryInterval);
expiryInterval = undefined;
handleLogout();
}
}, 2000); // check token every 2 seconds
}

onMounted(() => {
if (isLoggedIn) {
const expire_timestamp = getAuthContext().expire_timestamp;

// In case there is no timestamp, something is wrong and you definitelly
// has to login again.
if (expire_timestamp === null) {
handleLogout();
return;
}

startTokenExpiryWatcher(expire_timestamp, handleLogout);
}
});

onUnmounted(() => {
if (expiryInterval) {
clearInterval(expiryInterval);
}
});
</script>

<style scoped>
Expand Down
Loading