Skip to content

Commit

Permalink
core: frontend: VersionChooser: Add docker login
Browse files Browse the repository at this point in the history
* Add docker login component modal
* Connect docker login to VersionChooser page
* Modify version chooser (Service/Types) files to include new docker
  login API
  • Loading branch information
JoaoMario109 authored and patrickelectric committed Jan 30, 2025
1 parent a7e47b0 commit e8089c3
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 1 deletion.
266 changes: 266 additions & 0 deletions core/frontend/src/components/version-chooser/DockerLogin.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
<template>
<v-card>
<v-card-subtitle class="text-center pt-6">
Log in using your Docker Hub credentials to access private repositories
and benefit from an increased rate limit.
</v-card-subtitle>

<SpinningLogo
v-if="op_loading"
size="100px"
:subtitle="op_description"
/>
<div v-else>
<v-tabs
v-model="tab"
fixed-tabs
@change="onTabChange"
>
<v-tab key="0" href="#0" class="tab-text">
<v-icon class="mr-3">
mdi-login
</v-icon>
Login
</v-tab>
<v-tab key="1" href="#1" class="tab-text">
<v-icon class="mr-3">
mdi-account
</v-icon>
Accounts
</v-tab>
</v-tabs>
<v-card-text v-if="is_login_tab">
<div class="d-flex justify-space-around">
<v-switch
v-model="log_in_info.root"
label="Login root user"
class="pb-3"
/>
<v-switch
v-model="use_custom_registry"
label="Custom registry"
class="pb-3"
/>
</div>

<v-text-field
v-if="use_custom_registry"
v-model="log_in_info.registry"
class="pb-3"
label="Custom Registry"
outlined
hide-details
/>

<v-text-field
v-model="log_in_info.username"
class="pb-3"
label="Docker Username"
outlined
hide-details
/>

<v-text-field
v-model="log_in_info.password"
label="Docker Password or PAT"
type="password"
outlined
hide-details
/>
</v-card-text>
<v-card-text v-if="is_accounts_tab">
<v-list
v-if="has_some_account"
>
<v-list-item
v-for="account in accounts"
:key="account.username"
>
<v-list-item-content>
<v-list-item-title>
{{ account.username }}
</v-list-item-title>
<v-list-item-subtitle>
{{ account.registry }}
</v-list-item-subtitle>
</v-list-item-content>
<div v-if="account.root">
<v-tooltip bottom>
<template #activator="{ on }">
<v-icon v-on="on">
mdi-shield-account
</v-icon>
</template>
<span>Used by root user</span>
</v-tooltip>
</div>
<v-list-item-action>
<v-btn
icon
@click="logout(account.username, account.registry)"
>
<v-icon>mdi-logout</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list>
<v-card-subtitle
v-else
class="text-center pt-6"
>
No connected accounts
</v-card-subtitle>
</v-card-text>
</div>
<v-alert
v-if="has_error"
class="mx-4"
type="error"
>
{{ op_error }}
</v-alert>
<v-alert
v-if="has_success"
class="mx-4"
type="success"
>
{{ op_success }}
</v-alert>
<v-card-actions class="justify-center pb-4">
<v-btn
color="primary"
@click="$emit('cancel')"
>
Cancel
</v-btn>
<v-spacer />
<v-btn
color="success"
@click="login"
>
Login
</v-btn>
</v-card-actions>
</v-card>
</template>

<script lang="ts">
import Vue from 'vue'
import SpinningLogo from '@/components/common/SpinningLogo.vue'
import { DockerLoginInfo } from '@/types/version-chooser'
import { dockerAccounts, dockerLogin, dockerLogout } from '@/utils/version_chooser'
const DEFAULT_DOCKER_REGISTRY = 'https://index.docker.io/v1/'
const DEFAULT_LOG_IN_INFO = {
root: false,
registry: DEFAULT_DOCKER_REGISTRY,
username: '',
password: '',
} as DockerLoginInfo
export default Vue.extend({
name: 'VersionCard',
components: {
SpinningLogo,
},
data() {
return {
tab: '0',
log_in_info: { ...DEFAULT_LOG_IN_INFO },
use_custom_registry: false,
accounts: [] as DockerLoginInfo[],
op_loading: false,
op_description: '',
op_error: undefined as string | undefined,
op_success: undefined as string | undefined,
}
},
computed: {
has_error(): boolean {
return this.op_error !== undefined
},
has_success(): boolean {
return this.op_success !== undefined
},
has_some_account(): boolean {
return this.accounts.length > 0
},
is_login_tab(): boolean {
return this.tab === '0'
},
is_accounts_tab(): boolean {
return this.tab === '1'
},
},
async mounted() {
await this.fetchAccounts()
},
methods: {
cleanAlerts() {
this.op_error = undefined
this.op_success = undefined
},
prepareOperation(description: string) {
this.op_error = undefined
this.op_success = undefined
this.op_loading = true
this.op_description = description
},
resetInput() {
this.log_in_info = { ...DEFAULT_LOG_IN_INFO }
this.use_custom_registry = false
},
async onTabChange() {
this.cleanAlerts()
await this.fetchAccounts()
},
async login() {
this.prepareOperation('Logging in to Docker Hub...')
try {
await dockerLogin(this.log_in_info)
this.resetInput()
this.op_success = 'Successfully added account in Docker Daemon.'
} catch (op_error) {
this.op_error = String(op_error ?? 'Failed to login to Docker Hub.')
} finally {
this.op_loading = false
}
},
async logout(username: string, registry?: string) {
this.prepareOperation('Logging out from Docker Hub...')
try {
const data = {
username,
registry,
} as DockerLoginInfo
await dockerLogout(data)
await this.fetchAccounts()
} catch (op_error) {
this.op_error = String(op_error ?? 'Failed to logout to Docker Hub.')
} finally {
this.op_loading = false
}
},
async fetchAccounts() {
this.prepareOperation('Fetching connected accounts...')
try {
this.accounts = await dockerAccounts()
} catch (op_error) {
this.op_error = String(op_error ?? 'Failed to list connected accounts.')
} finally {
this.op_loading = false
}
},
},
})
</script>
21 changes: 20 additions & 1 deletion core/frontend/src/components/version-chooser/VersionChooser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
max-width="1000"
class="mx-auto"
>
<v-dialog
v-model="show_docker_login_dialog"
max-width="450"
>
<DockerLogin
@cancel="show_docker_login_dialog = false"
/>
</v-dialog>
<v-card
v-if="!settings.is_pirate_mode"
max-width="900"
Expand Down Expand Up @@ -77,7 +85,15 @@
max-width="900"
class="mx-auto my-12 pa-4"
>
<h2>Remote Versions</h2>
<div class="d-flex justify-space-between pb-3">
<h2>Remote Versions</h2>
<v-btn
color="primary"
@click="show_docker_login_dialog = true"
>
Docker Login
</v-btn>
</div>
<v-form
@submit.prevent="loadVersions()"
>
Expand Down Expand Up @@ -211,13 +227,15 @@ import PullTracker from '@/utils/pull_tracker'
import * as VCU from '@/utils/version_chooser'
import SpinningLogo from '../common/SpinningLogo.vue'
import DockerLogin from './DockerLogin.vue'
import VersionCard from './VersionCard.vue'
const notifier = new Notifier(version_chooser_service)
export default Vue.extend({
name: 'VersionChooser',
components: {
DockerLogin,
SpinningLogo,
VersionCard,
PullProgress,
Expand Down Expand Up @@ -257,6 +275,7 @@ export default Vue.extend({
selected_image: default_repository,
deleting: '', // image currently being deleted, if any
file_input_error: '',
show_docker_login_dialog: false,
}
},
computed: {
Expand Down
7 changes: 7 additions & 0 deletions core/frontend/src/types/version-chooser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ export interface ServerResponse {
type: string,
}

export interface DockerLoginInfo {
root?: boolean
registry?: string
username: string
password?: string
}

export function isServerResponse(response: unknown): response is ServerResponse {
// eslint-disable-next-line no-extra-parens
return (response as ServerResponse).status !== undefined
Expand Down
29 changes: 29 additions & 0 deletions core/frontend/src/utils/version_chooser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { gt as sem_ver_greater, SemVer } from 'semver'
import Notifier from '@/libs/notifier'
import { version_chooser_service } from '@/types/frontend_services'
import {
DockerLoginInfo,
LocalVersionsQuery, Version, VersionsQuery, VersionType,
} from '@/types/version-chooser'
import back_axios from '@/utils/api'
Expand Down Expand Up @@ -173,8 +174,36 @@ async function loadBootstrapCurrentVersion(): Promise<string | undefined> {
})
}

async function dockerLogin(info: DockerLoginInfo): Promise<void> {
await back_axios({
method: 'post',
url: `${API_URL}/docker/login/`,
data: info,
})
}

async function dockerLogout(info: DockerLoginInfo): Promise<void> {
await back_axios({
method: 'post',
url: `${API_URL}/docker/logout/`,
data: info,
})
}

async function dockerAccounts(): Promise<DockerLoginInfo[]> {
const data = await back_axios({
method: 'get',
url: `${API_URL}/docker/accounts/`,
})

return data.data as DockerLoginInfo[]
}

export {
DEFAULT_REMOTE_IMAGE,
dockerAccounts,
dockerLogin,
dockerLogout,
fixVersion,
getLatestBeta,
getLatestStable,
Expand Down

0 comments on commit e8089c3

Please sign in to comment.