Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VPN #288

Draft
wants to merge 30 commits into
base: master
Choose a base branch
from
Draft

VPN #288

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d6e8899
feat: begin adding gpu views
annybs Feb 8, 2025
f4ccd1c
chore: copy EditableTitle, Slider from vpn branch
annybs Feb 8, 2025
11b5966
feat: add sliders to gpu deploy
annybs Feb 8, 2025
bfc76bf
feat: complete deploy form
annybs Feb 17, 2025
ebbf330
feat: begin to integrate gpu endpoints
annybs Feb 24, 2025
96a4545
feat: integrate gpu list view
annybs Feb 24, 2025
f38db8b
feat: misc development
annybs Feb 24, 2025
3000fa6
feat: add gpu overview
annybs Feb 24, 2025
3ef9b28
feat: working power toggle, misc fixes
annybs Feb 24, 2025
be39e28
feat: more polling fixes, add ip address copy
annybs Feb 24, 2025
70afe71
fix: gpu list design
annybs Feb 24, 2025
4d24187
fix: flickering while polling; name change validation
annybs Feb 24, 2025
1471e80
fix: clear validation error on name edit
annybs Feb 24, 2025
1a0e41e
feat: implement destroy gpu flow
annybs Feb 24, 2025
35f53fe
feat: begin to add api key integration
annybs Feb 25, 2025
a4b7c29
feat: improve error handling in deploy, destroy
annybs Mar 3, 2025
a745762
feat: create tabulated settings view, replaces account
annybs Mar 3, 2025
3644db3
feat: visual improvements to api keys panel
annybs Mar 3, 2025
0d84a64
Merge pull request #291 from edge/feature/gpu
annybs Mar 3, 2025
94bac27
feat: improve error display, grid layout for api keys
annybs Mar 4, 2025
13baf64
chore: update account-utils
annybs Jan 6, 2025
2c68990
chore: update account-utils
annybs Jan 7, 2025
ca7e622
feat: add vpn list view
annybs Mar 4, 2025
70116bd
feat: vpn deploy view, detail view wip
annybs Mar 4, 2025
1d6e5d9
tweak: improve usage of model in EditableTitle
annybs Mar 4, 2025
f8ded4f
feat: vpn overview
annybs Feb 3, 2025
6ca27e7
feat: add metrics tab
annybs Feb 3, 2025
fe4086c
wip speed slider
annybs Feb 3, 2025
2e196c1
wip unsaved changes
annybs Feb 3, 2025
6a0110f
wip vpn update view
annybs Feb 4, 2025
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
11 changes: 5 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"main": "server/index.js",
"dependencies": {
"@chenfengyuan/vue-qrcode": "^2.0.0",
"@edge/account-utils": "^0.4.1",
"@edge/account-utils": "github:edge/account-utils#feature/vpn",
"@edge/cache-config": "^1.1.0",
"@edge/index-utils": "^0.6.2",
"@headlessui/vue": "^1.7.3",
Expand Down
5 changes: 5 additions & 0 deletions src/components/MobileNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export default {
link: '/servers',
text: 'Servers'
},
{
_key: 'gpu',
link: '/gpus',
text: 'GPUs'
},
{
_key: 'dns',
link: '/domains',
Expand Down
10 changes: 10 additions & 0 deletions src/components/SideNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ export default {
link: '/servers',
text: 'Servers'
},
{
_key: 'gpu',
link: '/gpus',
text: 'GPUs'
},
{
_key: 'vpn',
link: '/vpns',
text: 'VPN'
},
{
_key: 'dns',
link: '/domains',
Expand Down
2 changes: 1 addition & 1 deletion src/components/UserMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</MenuItem>
<MenuItem v-slot="{ active }">
<button
@click.prevent="navigate('/account')"
@click.prevent="navigate({ name: 'Settings' })"
:class="[
'menu__item',
active ? 'active' : ''
Expand Down
4 changes: 2 additions & 2 deletions src/components/billing/Invoices.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ const isCryptoView = Boolean(store.state.account?.useCryptoView)
If you add an email address to your account, you will receive an email notification when a new invoice is generated and whenever your account status changes.
You can also use your email address to log in to your Edge Account, keeping your account number secure.
<span v-if="!accountEmail">
<RouterLink :to="{ name: 'Account' }" class="text-green">Add an email address</RouterLink>.
<RouterLink :to="{ name: 'Settings' }" class="text-green">Add an email address</RouterLink>.
</span>
<span v-else>
Your email address is <span class="text-green">{{ accountEmail }}</span>.
You can <RouterLink :to="{ name: 'Account' }" class="text-green">change your email address</RouterLink> if you need to.
You can <RouterLink :to="{ name: 'Settings' }" class="text-green">change your email address</RouterLink> if you need to.
</span>
</p>
<p>
Expand Down
2 changes: 1 addition & 1 deletion src/components/billing/PaymentCards.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<RouterLink :to="{ name: 'Wallet' }" class="text-green">View your wallet</RouterLink> to start funding your account manually.
</p>
<p v-else>
<RouterLink :to="{ name: 'Account' }" class="text-green">Enable Crypto View</RouterLink> to start funding your account manually.
<RouterLink :to="{ name: 'Settings' }" class="text-green">Enable Crypto View</RouterLink> to start funding your account manually.
</p>
</section>
</article>
Expand Down
2 changes: 1 addition & 1 deletion src/components/billing/Wallet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async function startPurchase() {
You're currently using the crypto view, which provides comprehensive visibility of your wallet and transactions on the XE blockchain.
This page enables you to manage your account balance directly using XE.
If you're new to cryptocurrency, you can <a href="https://wiki.edge.network/getting-and-storing-tokens/wallets" target="_blank" class="text-green">learn more about XE on the Edge Wiki</a>.
Alternatively, if you prefer a more streamlined interface, you can <RouterLink :to="{ name: 'Account' }" class="text-green">disable the crypto view</RouterLink>.
Alternatively, if you prefer a more streamlined interface, you can <RouterLink :to="{ name: 'Settings' }" class="text-green">disable the crypto view</RouterLink>.
</p>
</section>
</article>
Expand Down
5 changes: 4 additions & 1 deletion src/components/server/ServerPowerToggle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import { mapGetters, mapState } from 'vuex'

export default {
name: 'ServerPowerToggle',
props: ['activeTasks', 'disableActions', 'server'],
props: ['disableActions', 'server'],
data() {
return {
showConfirmationModal: false,
Expand All @@ -71,6 +71,9 @@ export default {
computed: {
...mapGetters(['balanceSuspend']),
...mapState(['session']),
activeTasks() {
return this.$store.getters.tasksByServerId(this.serverId)
},
destroying() {
return this.activeTasks.some(task => task.action === 'destroy')
},
Expand Down
98 changes: 98 additions & 0 deletions src/layout/EditableTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<script setup>
import LoadingSpinner from '@/components/icons/LoadingSpinner.vue'
import { CheckIcon, PencilIcon, XIcon } from '@heroicons/vue/outline'
import { defineEmits, defineModel, defineProps, ref, watchPostEffect } from 'vue'

const emit = defineEmits(['cancel', 'edit','save'])

const props = defineProps({
busy: Boolean,
disabled: Boolean,
invalid: Boolean,
placeholder: String
})

const editing = ref(false)
const input = ref(null)
const model = defineModel()
const previousValue = ref(model.value)

function cancel() {
editing.value = false
model.value = previousValue.value

emit('cancel')
}

function edit() {
editing.value = true

emit('edit')
}

function save() {
if (props.invalid) return

editing.value = false
previousValue.value = model.value

emit('save')
}

watchPostEffect(() => {
if (input.value) {
input.value.focus()
}
})
</script>

<template>
<div v-if="editing" class="editable-title__container flex">
<input
v-model="model"
@keypress.enter="save"
class="editable-title__value"
:placeholder="placeholder"
ref="input"
type="text"
/>

<div class="mt-3 flex">
<button class="ml-2" @click="save" :disabled="invalid || disabled">
<CheckIcon class="button__icon text-green hover:text-green-600" />
</button>
<button @click="cancel" class="ml-2">
<XIcon class="button__icon text-red hover:text-red-600" />
</button>
</div>
</div>

<div v-else class="editable-title__container flex">
<h1 class="w-max mb-0">{{ model }}</h1>

<div class="mt-3">
<button v-if="busy" class="ml-2" disabled>
<LoadingSpinner />
</button>
<button v-else class="ml-2" @click="edit" :disabled="disabled">
<PencilIcon class="button__icon text-gray-400 hover:text-green" />
</button>
</div>
</div>
</template>

<style>
.editable-title__value {
@apply bg-transparent text-3xl text-gray-600 border-b border-gray-400 w-full;
@apply focus:outline-none focus:border-green focus:text-green;
}

.editable-title__container .button__icon {
@apply w-5 ml-1;
}

.editable-title__container button:disabled,
.editable-title__container button:disabled .button__icon {
@apply text-gray-400 hover:no-underline;
}
</style>
35 changes: 35 additions & 0 deletions src/layout/ErrorV2.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script setup>
import { ExclamationIcon } from '@heroicons/vue/outline'
import { computed, defineProps } from 'vue'

const props = defineProps({
error: Object
})

const message = computed(() => {
if (!props.error) return ''

if (props.error && props.error.response && props.error.response.body) {
const body = props.error.response.body
if (body.hint) return body.hint
if (body.reason) return body.reason
if (body.error) return body.error
if (body.message) return body.message
}

if (props.error.value && props.error.message) {
return props.error.message
}

return 'Unknown error'
})
</script>

<template>
<div v-if="error" class="error_wrapper block errorMessage">
<div class="float-left">
<ExclamationIcon class="w-3.5 text-red" />
</div>
<span class="errorMessage__text text-red">{{ message }}</span>
</div>
</template>
46 changes: 46 additions & 0 deletions src/layout/PowerToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script setup>
import LoadingSpinner from '../components/icons/LoadingSpinner.vue'
import { Switch } from '@headlessui/vue'
import { defineEmits, defineModel, defineProps } from 'vue'

const emit = defineEmits(['click'])

/** v-model must be a Boolean ref! */
const model = defineModel()

defineProps({
busy: Boolean,
disabled: Boolean,
offText: String,
on: Boolean,
onText: String
})
</script>

<template>
<Switch
v-model="model"
class="relative inline-flex flex-shrink-0 transition-colors duration-200 ease-in-out border-2 border-transparent rounded-full cursor-pointer w-16 md:w-20 h-7 md:h-8 focus:outline-none"
:class="model ? 'bg-green' : 'bg-gray-300'"
:disabled="busy || disabled"
@click="emit('click', !model)"
>
<span class="sr-only">Status</span>

<span v-if="model" class="absolute w-full h-full flex items-center text-xs leading-none justify-start text-white pl-3 md:pl-4">
<span v-if="busy">
<LoadingSpinner />
</span>
<span v-else>{{ onText || 'ON' }}</span>
</span>
<span v-else class="absolute w-full h-full flex items-center text-xs leading-none justify-end text-gray-500 pr-2.5 md:pr-3.5">
<span v-if="busy">
<LoadingSpinner />
</span>
<span v-else>{{ offText || 'OFF' }}</span>
</span>

<span v-if="model" aria-hidden class="translate-x-9 md:translate-x-12 transform inline-block w-6 h-6 md:w-7 md:h-7 transition duration-200 ease-in-out bg-white rounded-full shadow-lg pointer-events-none ring-0"/>
<span v-else aria-hidden class="translate-x-0 transform inline-block w-6 h-6 md:w-7 md:h-7 transition duration-200 ease-in-out bg-white rounded-full shadow-lg pointer-events-none ring-0"/>
</Switch>
</template>
60 changes: 60 additions & 0 deletions src/layout/Slider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script setup>
import 'vue-slider-component/theme/antd.css'
import VueSlider from 'vue-slider-component'
import { defineEmits, defineModel, defineProps } from 'vue'

defineProps({
disabled: Boolean,
formatter: Function,
marks: Object,
max: Number,
min: Number,
title: String,
tooltip: String
})

const emit = defineEmits(['change'])

const model = defineModel()
</script>

<template>
<div class="slider__container" :class="{ disabled }">
<span v-if="title" class="slider__title">{{ title }}</span>
<vue-slider
v-model="model"
adsorb
tooltipPlacement="top"
width="100%"
:contained="true"
:disabled="disabled"
:dot-style="{ background: '#4ecd5f', boxShadow: '0 0 2px 1px #eee', border: 'none' }"
:dotSize="20"
:label-style="{ color: '#999', fontSize: '12px' }"
:marks="marks"
:max="max"
:min="min"
:process-style="{ background: '#4ecd5f' }"
:step-active-style="{ background: '#fff', opacity: '1', border: 'none', boxShadow: 'rgba(0, 0, 0, 0.24) 0px 3px 8px' }"
:tooltip="tooltip || 'hover'"
:tooltip-formatter="formatter"
:tooltip-style="{ background: '#4ecd5f', borderColor: '#4ecd5f' }"
@change="val => emit('change', val)"
/>
</div>
</template>

<style>
.slider__container {
@apply relative flex space-x-3 items-start justify-center pr-5 pl-2 pt-14 pb-8 border border-gray-300 rounded-md;
}

.slider__container.disabled {
@apply cursor-not-allowed opacity-50;
}

.slider__title {
@apply absolute top-0 inline-block px-3 text-gray-500 transform -translate-y-1/2 bg-white;
}

</style>
Loading