Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
44 changes: 44 additions & 0 deletions client/public/img/icons/avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 14 additions & 7 deletions client/src/components/accounts/tokens.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
{{ item.id }}
</template>
<template v-slot:[`item.user.username`]="{ item }">
<span>{{ item.user.username }}</span>
<span>{{ item.user?.username }}</span>
</template>
<template v-slot:[`item.expiresAt`]="{ item }">
<span v-if="item.expiresAt">{{ new Date(item.expiresAt).toLocaleString() }}</span>
Expand All @@ -47,7 +47,7 @@
</template>
</v-data-table>

<!-- Button to add a token -->
<!-- Button to add a token
<div style="display: flex; justify-content: flex-end; margin-top: 16px;">
<v-btn
fab
Expand All @@ -60,12 +60,12 @@
</v-btn>
</div>

<!-- Dialog for a new Token -->
<!-- Dialog for a new Token
<v-dialog v-model="createDialog" max-width="500px">
<v-card>
<v-card-title>Create Token</v-card-title>
<v-card-text>
<v-text-field v-model="newToken.token" label="Token Value"></v-text-field>
<v-text-field v-model="newToken.name" label="Name"></v-text-field>
<v-text-field
v-model="newToken.expiresAt"
label="Expires At (ISO)"
Expand All @@ -79,6 +79,7 @@
</v-card-actions>
</v-card>
</v-dialog>
-->
</v-container>
</template>

Expand All @@ -91,18 +92,24 @@ export default defineComponent({
setup() {
interface Token {
id?: string;
token: string;
token?: string;
name: string;
expiresAt?: string;
userId?: string;
user?: {
id: string;
username: string;
};
}
const tokens = ref<Token[]>([])
const loading = ref(false)
const search = ref('')
const createDialog = ref(false)
const newToken = ref<Token>({ token: '', expiresAt: '', userId: '' })
const newToken = ref<Token>({ token: '', name: '', expiresAt: '', userId: '' })

const headers = [
{ title: 'Token ID', value: 'token' },
{ title: 'Name', value: 'name' },
{ title: 'Owner', value: 'user.username' },
{ title: 'Expires At', value: 'expiresAt' },
{ title: 'Actions', value: 'actions', sortable: false, align: 'end' as const },
Expand All @@ -129,7 +136,7 @@ export default defineComponent({
}

const openCreateDialog = () => {
newToken.value = { token: '', expiresAt: '', userId: '' }
newToken.value = { token: '', name: '', expiresAt: '', userId: '' }
createDialog.value = true
}

Expand Down
122 changes: 121 additions & 1 deletion client/src/components/profile/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@
<v-col cols="12">
<v-card class="pa-4">
<h3 class="mb-4">API Tokens</h3>
<div style="display: flex; justify-content: flex-end; margin-bottom: 8px;">
<v-btn
fab
color="primary"
style="margin-right: 6px;"
@click="openCreateDialog"
>
<v-icon>mdi-plus</v-icon>
<span class="sr-only">Create Token</span>
</v-btn>
</div>
<v-table density="compact">
<thead>
<tr>
Expand Down Expand Up @@ -126,6 +137,63 @@
</tr>
</tbody>
</v-table>
<v-dialog v-model="createDialog" max-width="500px">
<v-card>
<v-card-title>Create Token</v-card-title>
<v-card-text>
<v-text-field v-model="newToken.name" label="Name"></v-text-field>
<v-text-field
v-model="newToken.expiresAt"
label="Expires At (ISO)"
type="datetime-local"
></v-text-field>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="createDialog = false">Abort</v-btn>
<v-btn color="primary" @click="saveCreate">Create</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="tokenDialog" max-width="500px">
<v-card>
<v-card-title>Token Details</v-card-title>
<v-card-text>
<v-alert type="warning" density="compact" class="mb-2">
This token will <strong>not be shown again</strong>. Please copy and store it securely now.
</v-alert>
<v-textarea
v-model="generatedToken.token"
label="Token"
auto-grow
readonly
rows="3"
class="mb-2"
:class="{ 'flash': textareaFlash }"
></v-textarea>
<v-btn
color="primary"
@click="copyToken"
class="mb-2"
>
<v-icon left>mdi-content-copy</v-icon>
Copy Token
</v-btn>
<v-snackbar v-model="textareaFlash" timeout="3000">
Token copied to clipboard!
<template #action="{ attrs }">
<v-btn text v-bind="attrs" @click="textareaFlash = false">
Close
</v-btn>
</template>
</v-snackbar>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="tokenDialog = false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-card>
</v-col>
</v-row>
Expand All @@ -150,10 +218,15 @@ export default defineComponent({
provider: '',
lastLogin: null,
})
const defaultAvatar = '/avatar.svg'
const defaultAvatar = '/img/icons/avatar.svg'
const tokens = ref<any[]>([])
const editAvatarDialog = ref(false)
const avatarFile = ref<File | null>(null)
const createDialog = ref(false)
const tokenDialog = ref(false)
const generatedToken = ref<any>({ name: '', expiresAt: '', token: '' })
const newToken = ref<any>({ name: '', expiresAt: '' })
const textareaFlash = ref(false)

const loadProfile = async () => {
try {
Expand Down Expand Up @@ -197,6 +270,34 @@ export default defineComponent({
}
}

const openCreateDialog = () => {
newToken.value = { name: '', expiresAt: '', token: '' }
createDialog.value = true
}

const saveCreate = async () => {
try {
const response = await axios.post('/api/tokens', newToken.value)
generatedToken.value = response.data
await loadTokens()
createDialog.value = false
tokenDialog.value = true
console.log('Token created:', newToken)
} catch (e) {
// error handling
}
}

const copyToken = () => {
if (generatedToken.value.token) {
navigator.clipboard.writeText(generatedToken.value.token)
textareaFlash.value = true
setTimeout(() => {
textareaFlash.value = false
}, 300)
}
}

onMounted(() => {
loadProfile()
loadTokens()
Expand All @@ -210,7 +311,26 @@ export default defineComponent({
editAvatarDialog,
avatarFile,
saveAvatar,
createDialog,
tokenDialog,
generatedToken,
newToken,
openCreateDialog,
saveCreate,
copyToken,
textareaFlash,
}
},
})
</script>

<style scoped>
.flash {
animation: flash-animation 3s ease-in-out;
}

@keyframes flash-animation {
0% { background-color: rgba(255, 255, 0, 0.3); }
100% { background-color: transparent; }
}
</style>
2 changes: 1 addition & 1 deletion client/src/layouts/default/NavDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
>
<template #prepend>
<v-avatar size="30">
<v-img :src="userAvatar || '/avatar.svg'" alt="User avatar" />
<v-img :src="userAvatar || '/img/icons/avatar.svg'" alt="User avatar" />
</v-avatar>
</template>
<template #title>
Expand Down
5 changes: 2 additions & 3 deletions server/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ NODE_ENV=production
KUBERO_WEBHOOK_SECRET=mysecret

KUBERO_AUTHENTICATION=false
KUBERO_JWT_SECRET="A long random string that should be kept secret"
KUBERO_JWT_EXPIRESIN=36000s
KUBERO_JWT_ISSUER=kubero
JWT_SECRET="A long random string that should be kept secret"
JWT_EXPIRESIN=36000s



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ CREATE TABLE "Role" (
-- CreateTable
CREATE TABLE "Token" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT,
"userId" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expiresAt" DATETIME NOT NULL,
Expand All @@ -83,6 +84,69 @@ CREATE TABLE "Permission" (
"updatedAt" DATETIME NOT NULL
);

-- CreateTable
CREATE TABLE "Buildpack" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"language" TEXT NOT NULL,
"fetchId" TEXT NOT NULL,
"buildId" TEXT NOT NULL,
"runId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Buildpack_fetchId_fkey" FOREIGN KEY ("fetchId") REFERENCES "BuildpackPhase" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Buildpack_buildId_fkey" FOREIGN KEY ("buildId") REFERENCES "BuildpackPhase" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "Buildpack_runId_fkey" FOREIGN KEY ("runId") REFERENCES "BuildpackPhase" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "BuildpackPhase" (
"id" TEXT NOT NULL PRIMARY KEY,
"repository" TEXT NOT NULL,
"tag" TEXT NOT NULL,
"command" TEXT,
"readOnlyAppStorage" BOOLEAN NOT NULL,
"securityContextId" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "BuildpackPhase_securityContextId_fkey" FOREIGN KEY ("securityContextId") REFERENCES "SecurityContext" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "SecurityContext" (
"id" TEXT NOT NULL PRIMARY KEY,
"runAsUser" INTEGER NOT NULL,
"runAsGroup" INTEGER NOT NULL,
"runAsNonRoot" BOOLEAN NOT NULL,
"readOnlyRootFilesystem" BOOLEAN NOT NULL,
"allowPrivilegeEscalation" BOOLEAN NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);

-- CreateTable
CREATE TABLE "Capability" (
"id" TEXT NOT NULL PRIMARY KEY,
"securityCtxId" TEXT NOT NULL,
CONSTRAINT "Capability_securityCtxId_fkey" FOREIGN KEY ("securityCtxId") REFERENCES "SecurityContext" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "CapabilityAdd" (
"id" TEXT NOT NULL PRIMARY KEY,
"value" TEXT NOT NULL,
"capabilityId" TEXT NOT NULL,
CONSTRAINT "CapabilityAdd_capabilityId_fkey" FOREIGN KEY ("capabilityId") REFERENCES "Capability" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "CapabilityDrop" (
"id" TEXT NOT NULL PRIMARY KEY,
"value" TEXT NOT NULL,
"capabilityId" TEXT NOT NULL,
CONSTRAINT "CapabilityDrop_capabilityId_fkey" FOREIGN KEY ("capabilityId") REFERENCES "Capability" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateTable
CREATE TABLE "_UserToUserGroup" (
"A" TEXT NOT NULL,
Expand Down
Loading
Loading