diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 37c901a82..1c43d5312 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -70,15 +70,13 @@ services: volumes: - ./www/webapp/:/usr/src/app/ working_dir: /usr/src/app/ - command: bash -c "npm install && npm run serve --fix" + command: bash -c "npm install && npm run dev -- --host" environment: - - CODESANDBOX_SSE=1 # so that ws: protocol ends up as auto: in node_modules/@vue/cli-service/lib/commands/serve.js - - CYPRESS_CACHE_FOLDER=/tmp/.cache # https://github.com/cypress-io/cypress/issues/1281#issuecomment-404823001 - - VUE_APP_DESECSTACK_NS=${DESECSTACK_NS} - - VUE_APP_DESECSTACK_API_SEPA_CREDITOR_ID=${DESECSTACK_API_SEPA_CREDITOR_ID} - - VUE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME=${DESECSTACK_API_SEPA_CREDITOR_NAME} - - VUE_APP_LOCAL_PUBLIC_SUFFIXES=dedyn.${DESECSTACK_DOMAIN} - - VUE_APP_EMAIL=support@desec.${DESECSTACK_DOMAIN} + - VITE_APP_DESECSTACK_NS=${DESECSTACK_NS} + - VITE_APP_DESECSTACK_API_SEPA_CREDITOR_ID=${DESECSTACK_API_SEPA_CREDITOR_ID} + - VITE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME=${DESECSTACK_API_SEPA_CREDITOR_NAME} + - VITE_APP_LOCAL_PUBLIC_SUFFIXES=dedyn.${DESECSTACK_DOMAIN} + - VITE_APP_EMAIL=support@desec.${DESECSTACK_DOMAIN} networks: - rearwebapp logging: diff --git a/www/Dockerfile b/www/Dockerfile index 0472208ac..bd7dac91f 100644 --- a/www/Dockerfile +++ b/www/Dockerfile @@ -14,14 +14,14 @@ ARG DESECSTACK_API_SEPA_CREDITOR_ID ARG DESECSTACK_API_SEPA_CREDITOR_NAME ARG DESECSTACK_DOMAIN ARG DESECSTACK_NS -ENV VUE_APP_DESECSTACK_NS=${DESECSTACK_NS} \ - VUE_APP_DESECSTACK_API_SEPA_CREDITOR_ID=${DESECSTACK_API_SEPA_CREDITOR_ID} \ - VUE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME=${DESECSTACK_API_SEPA_CREDITOR_NAME} \ - VUE_APP_LOCAL_PUBLIC_SUFFIXES=dedyn.${DESECSTACK_DOMAIN} \ - VUE_APP_EMAIL=support@desec.${DESECSTACK_DOMAIN} +ENV VITE_APP_DESECSTACK_NS=${DESECSTACK_NS} \ + VITE_APP_DESECSTACK_API_SEPA_CREDITOR_ID=${DESECSTACK_API_SEPA_CREDITOR_ID} \ + VITE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME=${DESECSTACK_API_SEPA_CREDITOR_NAME} \ + VITE_APP_LOCAL_PUBLIC_SUFFIXES=dedyn.${DESECSTACK_DOMAIN} \ + VITE_APP_EMAIL=support@desec.${DESECSTACK_DOMAIN} COPY webapp/ /usr/src/app/ -RUN npm run build -- --no-clean +RUN npm run build -- --emptyOutDir false FROM nginx:mainline-alpine diff --git a/www/webapp/.eslintrc.js b/www/webapp/.eslintrc.js index 60da170fe..a3e7cb51f 100644 --- a/www/webapp/.eslintrc.js +++ b/www/webapp/.eslintrc.js @@ -5,7 +5,6 @@ module.exports = { root: true, env: { browser: true, - node: true, // Can be removed after migration to vite. es2024: true, }, parserOptions: { diff --git a/www/webapp/README.md b/www/webapp/README.md index fca5fd285..491f62baf 100644 --- a/www/webapp/README.md +++ b/www/webapp/README.md @@ -7,7 +7,7 @@ npm install ### Compiles and hot-reloads for development ``` -npm run serve +npm run dev ``` ### Compiles and minifies for production @@ -21,4 +21,4 @@ npm run lint ``` ### Customize configuration -See [Configuration Reference](https://cli.vuejs.org/config/). +See [Configuration Reference](https://vitejs.dev/config/). diff --git a/www/webapp/babel.config.js b/www/webapp/babel.config.js deleted file mode 100644 index e9558405f..000000000 --- a/www/webapp/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [ - '@vue/cli-plugin-babel/preset' - ] -} diff --git a/www/webapp/public/index.html b/www/webapp/index.html similarity index 87% rename from www/webapp/public/index.html rename to www/webapp/index.html index 185d6721a..fb509da35 100644 --- a/www/webapp/public/index.html +++ b/www/webapp/index.html @@ -3,8 +3,8 @@ - - + + deSEC – Free Secure DNS @@ -26,6 +26,7 @@

🛑 Warning

+ diff --git a/www/webapp/package.json b/www/webapp/package.json index 242e003c0..9a6817b23 100644 --- a/www/webapp/package.json +++ b/www/webapp/package.json @@ -3,8 +3,9 @@ "version": "0.1.0", "private": true, "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", + "dev": "vite", + "serve": "vite preview", + "build": "vite build", "lint": "eslint --ignore-path .gitignore --no-fix src/**/*.{vue,js,json}", "lint:fix": "eslint --ignore-path .gitignore --fix src/**/*.{vue,js,json}" }, @@ -13,27 +14,24 @@ "@mdi/font": "^7.2.96", "@mdi/js": "~7.2.96", "axios": "^1.4.0", - "core-js": "^3.27.1", "date-fns": "^2.30.0", "pinia": "^2.0.30", "vue": "~2.7.14", "vue-router": "~3.6.5", "vuelidate": "^0.7.7", - "vuetify": "^2.6.13" + "vuetify": "^2.7.0" }, "devDependencies": { - "@vue/cli-plugin-babel": "^5.0.8", - "@vue/cli-plugin-router": "^5.0.8", - "@vue/cli-service": "^5.0.8", - "eslint": "^8.31.0", + "@vitejs/plugin-legacy": "^4.1.1", + "@vitejs/plugin-vue2": "^2.2.0", + "eslint": "^8.45.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-import": "^2.27.5", - "eslint-plugin-vue": "^9.8.0", + "eslint-plugin-vue": "^9.15.1", "eslint-plugin-vuetify": "^1.1.0", "sass": "~1.64.2", - "sass-loader": "^13.2.0", - "vue-cli-plugin-vuetify": "^2.5.8", - "vue-template-compiler": "^2.7.14", + "unplugin-vue-components": "^0.25.1", + "vite": "^4.4.5", "vuetify-loader": "~1.9.1" } } diff --git a/www/webapp/postcss.config.js b/www/webapp/postcss.config.js deleted file mode 100644 index 961986e2b..000000000 --- a/www/webapp/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {} - } -} diff --git a/www/webapp/src/App.vue b/www/webapp/src/App.vue index 40b823a1d..e5e2fb4e0 100644 --- a/www/webapp/src/App.vue +++ b/www/webapp/src/App.vue @@ -199,7 +199,7 @@ export default { data: () => ({ user: useUserStore(), drawer: false, - email: process.env.VUE_APP_EMAIL, + email: import.meta.env.VITE_APP_EMAIL, mdiHeart, mdiMenuDown, tabmenu: { diff --git a/www/webapp/src/components/ActivateAccountActionHandler.vue b/www/webapp/src/components/ActivateAccountActionHandler.vue index 4dd1ced6b..d64f5dd89 100644 --- a/www/webapp/src/components/ActivateAccountActionHandler.vue +++ b/www/webapp/src/components/ActivateAccountActionHandler.vue @@ -78,7 +78,7 @@ data: () => ({ auto_submit: true, captchaWorking: false, - LOCAL_PUBLIC_SUFFIXES: process.env.VUE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), + LOCAL_PUBLIC_SUFFIXES: import.meta.env.VITE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), captcha: null, captcha_required: false, diff --git a/www/webapp/src/components/DonateDirectDebitForm.vue b/www/webapp/src/components/DonateDirectDebitForm.vue index 49d4d15ea..f704400ec 100644 --- a/www/webapp/src/components/DonateDirectDebitForm.vue +++ b/www/webapp/src/components/DonateDirectDebitForm.vue @@ -128,8 +128,8 @@ mdiEmail, /* from env */ - creditorid: process.env.VUE_APP_DESECSTACK_API_SEPA_CREDITOR_ID, - creditorname: process.env.VUE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME, + creditorid: import.meta.env.VITE_APP_DESECSTACK_API_SEPA_CREDITOR_ID, + creditorname: import.meta.env.VITE_APP_DESECSTACK_API_SEPA_CREDITOR_NAME, /* account holder name field */ name: '', diff --git a/www/webapp/src/router/index.js b/www/webapp/src/router/index.js index cc9a43ff6..f2671ac67 100644 --- a/www/webapp/src/router/index.js +++ b/www/webapp/src/router/index.js @@ -15,24 +15,24 @@ const routes = [ // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. - component: () => import(/* webpackChunkName: "signup" */ '@/views/SignUp.vue') + component: () => import('@/views/SignUp.vue'), }, { path: '/custom-setup/:domain', name: 'customSetup', - component: () => import(/* webpackChunkName: "signup" */ '@/views/DomainSetupPage.vue'), + component: () => import('@/views/DomainSetupPage.vue'), props: true, }, { path: '/dyn-setup/:domain', alias: '/dynsetup/:domain', name: 'dynSetup', - component: () => import(/* webpackChunkName: "signup" */ '@/views/DynSetup.vue') + component: () => import('@/views/DynSetup.vue'), }, { path: '/welcome/:domain?', name: 'welcome', - component: () => import(/* webpackChunkName: "signup" */ '@/views/WelcomePage.vue'), + component: () => import('@/views/WelcomePage.vue'), }, { path: 'https://desec.readthedocs.io/', @@ -47,47 +47,47 @@ const routes = [ { path: '/confirm/:action/:code', name: 'confirmation', - component: () => import(/* webpackChunkName: "account" */ '@/views/ConfirmationPage.vue'), + component: () => import('@/views/ConfirmationPage.vue') }, { path: '/reset-password/:email?', name: 'reset-password', - component: () => import(/* webpackChunkName: "account" */ '@/views/ResetPassword.vue') + component: () => import('@/views/ResetPassword.vue'), }, { path: '/totp/', name: 'totp', - component: () => import(/* webpackChunkName: "account" */ '@/views/CrudListTOTP.vue'), + component: () => import('@/views/CrudListTOTP.vue'), meta: {guest: false}, }, { path: '/totp-verify/', name: 'TOTPVerify', - component: () => import(/* webpackChunkName: "login" */ '@/views/Console/TOTPVerifyDialog.vue'), + component: () => import('@/views/Console/TOTPVerifyDialog.vue'), props: (route) => ({...route.params}), }, { path: '/mfa/', name: 'mfa', - component: () => import(/* webpackChunkName: "account" */ '@/views/MFA.vue'), + component: () => import('@/views/MFA.vue'), meta: {guest: false}, }, { path: '/change-email/:email?', name: 'change-email', - component: () => import(/* webpackChunkName: "account" */ '@/views/ChangeEmail.vue'), + component: () => import('@/views/ChangeEmail.vue'), meta: {guest: false}, }, { path: '/delete-account/', name: 'delete-account', - component: () => import(/* webpackChunkName: "account" */ '@/views/DeleteAccount.vue'), + component: () => import('@/views/DeleteAccount.vue'), meta: {guest: false}, }, { path: '/donate/', name: 'donate', - component: () => import(/* webpackChunkName: "extra" */ '@/views/DonatePage.vue'), + component: () => import('@/views/DonatePage.vue'), }, { path: 'https://github.com/desec-io/desec-stack/projects?query=is%3Aopen+sort%3Aname-asc&type=classic', @@ -97,51 +97,51 @@ const routes = [ { path: '/impressum/', name: 'impressum', - component: () => import(/* webpackChunkName: "extra" */ '@/views/ImpressumPage.vue'), + component: () => import('@/views/ImpressumPage.vue'), }, { path: '/privacy-policy/', name: 'privacy-policy', - component: () => import(/* webpackChunkName: "extra" */ '@/views/PrivacyPolicy.vue') + component: () => import('@/views/PrivacyPolicy.vue'), }, { path: '/terms/', name: 'terms', - component: () => import(/* webpackChunkName: "extra" */ '@/views/TermsPage.vue'), + component: () => import('@/views/TermsPage.vue'), }, { path: '/about/', name: 'about', - component: () => import(/* webpackChunkName: "extra" */ '@/views/AboutPage.vue'), + component: () => import('@/views/AboutPage.vue'), }, { path: '/login', name: 'login', - component: () => import(/* webpackChunkName: "login" */ '@/views/LoginPage.vue'), + component: () => import('@/views/LoginPage.vue'), }, { path: '/tokens', name: 'tokens', - component: () => import(/* webpackChunkName: "gui" */ '@/views/CrudListToken.vue'), + component: () => import('@/views/CrudListToken.vue'), meta: {guest: false}, }, { path: '/domains', name: 'domains', - component: () => import(/* webpackChunkName: "gui" */ '@/views/CrudListDomain.vue'), + component: () => import('@/views/CrudListDomain.vue'), meta: {guest: false}, }, { path: '/domains/:domain', name: 'domain', - component: () => import(/* webpackChunkName: "gui" */ '@/views/CrudListRecord.vue'), + component: () => import('@/views/CrudListRecord.vue'), meta: {guest: false}, }, ] const router = new VueRouter({ mode: 'history', - base: process.env.BASE_URL, + base: import.meta.env.BASE_URL, scrollBehavior (to, from) { // Skip if destination full path has query parameters and differs in no other way from previous if (from && Object.keys(to.query).length) { diff --git a/www/webapp/src/views/DomainSetup.vue b/www/webapp/src/views/DomainSetup.vue index 6f73a1296..3cf0e53a5 100644 --- a/www/webapp/src/views/DomainSetup.vue +++ b/www/webapp/src/views/DomainSetup.vue @@ -154,7 +154,7 @@ export default { }, ns: { type: Array, - default: () => process.env.VUE_APP_DESECSTACK_NS.split(' '), + default: () => import.meta.env.VITE_APP_DESECSTACK_NS.split(' '), }, }, data: () => ({ @@ -171,7 +171,7 @@ export default { snackbar_text: '', tab1: 'ns', tab2: 'ds', - LOCAL_PUBLIC_SUFFIXES: process.env.VUE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), + LOCAL_PUBLIC_SUFFIXES: import.meta.env.VITE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), }), computed: { tabs: function () { diff --git a/www/webapp/src/views/DynSetup.vue b/www/webapp/src/views/DynSetup.vue index e7dc1c317..b827479a1 100644 --- a/www/webapp/src/views/DynSetup.vue +++ b/www/webapp/src/views/DynSetup.vue @@ -191,7 +191,7 @@ errors: [], ips: undefined, token: undefined, - LOCAL_PUBLIC_SUFFIXES: process.env.VUE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), + LOCAL_PUBLIC_SUFFIXES: import.meta.env.VITE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '), lastChanged: undefined, }), async mounted() { diff --git a/www/webapp/src/views/HomePage.vue b/www/webapp/src/views/HomePage.vue index 58ab68f0d..524bbd788 100644 --- a/www/webapp/src/views/HomePage.vue +++ b/www/webapp/src/views/HomePage.vue @@ -246,7 +246,7 @@ export default { }, data: () => ({ mdiEmail, - contact_email: process.env.VUE_APP_EMAIL, + contact_email: import.meta.env.VITE_APP_EMAIL, contact_subject: 'Adopting of a Frontend Server', contact_body: 'Dear deSEC,\n\nI would like to adopt a frontend server in your networks!', domainType: null, diff --git a/www/webapp/src/views/ImpressumPage.vue b/www/webapp/src/views/ImpressumPage.vue index b537c9f09..eae2bae83 100644 --- a/www/webapp/src/views/ImpressumPage.vue +++ b/www/webapp/src/views/ImpressumPage.vue @@ -23,7 +23,7 @@ export default { name: 'ImpressumPage', data: () => ({ - email: process.env.VUE_APP_EMAIL, + email: import.meta.env.VITE_APP_EMAIL, }), } diff --git a/www/webapp/src/views/PrivacyPolicy.vue b/www/webapp/src/views/PrivacyPolicy.vue index 953ff5224..408dc4ecb 100644 --- a/www/webapp/src/views/PrivacyPolicy.vue +++ b/www/webapp/src/views/PrivacyPolicy.vue @@ -96,7 +96,7 @@ export default { name: 'PrivacyPolicy', data: () => ({ - email: process.env.VUE_APP_EMAIL, + email: import.meta.env.VITE_APP_EMAIL, privacy_policy: [ { title: 'Visiting this Website', diff --git a/www/webapp/src/views/SignUp.vue b/www/webapp/src/views/SignUp.vue index df63abc27..6813b78f2 100644 --- a/www/webapp/src/views/SignUp.vue +++ b/www/webapp/src/views/SignUp.vue @@ -178,7 +178,7 @@ import {digestError} from '@/utils'; import ErrorAlert from "@/components/ErrorAlert.vue"; - const LOCAL_PUBLIC_SUFFIXES = process.env.VUE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '); + const LOCAL_PUBLIC_SUFFIXES = import.meta.env.VITE_APP_LOCAL_PUBLIC_SUFFIXES.split(' '); const HTTP = axios.create({ baseURL: '/api/v1/', diff --git a/www/webapp/vite.config.js b/www/webapp/vite.config.js new file mode 100644 index 000000000..1ca35568e --- /dev/null +++ b/www/webapp/vite.config.js @@ -0,0 +1,29 @@ +/* eslint-env node */ +import {defineConfig} from 'vite' +import {resolve} from 'node:path'; +import Components from 'unplugin-vue-components/vite' +import {VuetifyResolver} from 'unplugin-vue-components/resolvers' +import legacy from '@vitejs/plugin-legacy' +import vue from '@vitejs/plugin-vue2' + +export default defineConfig({ + define: { + 'process.env.BUILD': '"web"' // fix for vuelidate@0.7.7 + }, + plugins: [ + vue(), + Components({ + resolvers: [VuetifyResolver()], + }), + legacy(), // Build for old browser. + ], + server: { + port: 8080, + }, + resolve: { + alias: [{ + find: '@', replacement: resolve(__dirname, 'src') + }], + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], + }, +}) diff --git a/www/webapp/vue.config.js b/www/webapp/vue.config.js deleted file mode 100644 index 0f36fe05f..000000000 --- a/www/webapp/vue.config.js +++ /dev/null @@ -1,17 +0,0 @@ -/** @type {import('@vue/cli-service').ProjectOptions} */ -module.exports = { - configureWebpack: { - devServer: { - allowedHosts: 'all', - }, - }, - chainWebpack: config => { - config.module - .rule('fonts') - .set('parser', { - dataUrlCondition: { - maxSize: 0 // Disable inline font to improve FCP (first contentful paint). - } - }); - }, -};