diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index 4edafd3f..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules
-packages
-dist
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 3e2c5d20..00000000
--- a/.eslintrc
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "env": {
- "node": true,
- "commonjs": true,
- "es6": true,
- "mocha": true
- },
- "globals": {
- "Atomics": "readonly",
- "SharedArrayBuffer": "readonly"
- },
- "parser": "vue-eslint-parser",
- "parserOptions": { "parser": "@typescript-eslint/parser" },
- "plugins": [
- "@typescript-eslint"
- ],
- "extends": [
- "eslint:recommended",
- "plugin:vue/vue3-essential",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "rules": {
- "linebreak-style": [
- "error",
- "unix"
- ],
- "quotes": [
- "error",
- "single"
- ],
- "semi": [
- "error",
- "always"
- ],
- "@typescript-eslint/explicit-module-boundary-types": "off",
- "no-console": "off"
- }
-}
diff --git a/.github/workflows/DevelopServerDeploy.yml b/.github/workflows/DevelopServerDeploy.yml
index af9adfa4..320d46cd 100644
--- a/.github/workflows/DevelopServerDeploy.yml
+++ b/.github/workflows/DevelopServerDeploy.yml
@@ -27,7 +27,7 @@ jobs:
run: npm run lint
- name: Test
- run: npm run test
+ run: npm run test --workspaces --if-present
build:
needs: test
@@ -45,23 +45,20 @@ jobs:
- name: Build action runner
run: docker build -t "wgd-action-runner:develop" --build-arg "GIT_SHA=${GITHUB_SHA}" apps/wgd-action-runner
- - name: Build hugo docs
- run: |
- docker run \
- -v "${GITHUB_WORKSPACE}/hugo:/site" \
- -v "${GITHUB_WORKSPACE}/website:/website" \
- -v "/var/www/dev.wikigdrive.com:/dist/hugo" \
- --env CONFIG_TOML="/site/config/_default/config.toml" --env BASE_URL="https://dev.wikigdrive.com" \
- wgd-action-runner:develop /steps/step_render_hugo
-
- - name: Copy index for vite
- run: mkdir -p ${GITHUB_WORKSPACE}/dist/hugo && cp -rf /var/www/dev.wikigdrive.com/* ${GITHUB_WORKSPACE}/dist/hugo
-
- uses: docker/build-push-action@v6
with:
tags: "wikigdrive-develop:${{ github.sha }},wikigdrive-develop:latest"
push: false
- build-args: "{'--build-arg': 'GIT_SHA=${{ github.sha }}'}"
+ build-args: |
+ GIT_SHA=${{ github.sha }}
+ BUILD_UI=yes
+
+ - name: Build docs
+ run: |
+ docker run \
+ -v "/var/www/dev.wikigdrive.com:/usr/src/app/website/.vitepress/dist" \
+ -e "GIT_SHA=${{ github.sha }}" \
+ "wikigdrive-develop:${{ github.sha }}" npm run build --workspace website
- name: Stop and remove
run: docker stop wikigdrive-develop ; docker rm wikigdrive-develop
diff --git a/.github/workflows/ProdServerDeploy.yml b/.github/workflows/ProdServerDeploy.yml
index 0b20b672..4c9ef19c 100644
--- a/.github/workflows/ProdServerDeploy.yml
+++ b/.github/workflows/ProdServerDeploy.yml
@@ -28,7 +28,7 @@ jobs:
run: npm run lint
- name: Test
- run: npm run test
+ run: npm run test --workspaces --if-present
build:
needs: test
@@ -46,23 +46,20 @@ jobs:
- name: Build action runner
run: docker build -t "wgd-action-runner:prod" --build-arg "GIT_SHA=${GITHUB_SHA}" apps/wgd-action-runner
- - name: Build hugo docs
- run: |
- docker run \
- -v "${GITHUB_WORKSPACE}/hugo:/site" \
- -v "${GITHUB_WORKSPACE}/website:/website" \
- -v "/var/www/wikigdrive.com:/dist/hugo" \
- --env CONFIG_TOML="/site/config/_default/config.toml" --env BASE_URL="https://wikigdrive.com" \
- wgd-action-runner:prod /steps/step_render_hugo
-
- - name: Copy index for vite
- run: mkdir -p ${GITHUB_WORKSPACE}/dist/hugo && cp -rf /var/www/wikigdrive.com/* ${GITHUB_WORKSPACE}/dist/hugo
-
- uses: docker/build-push-action@v6
with:
tags: "wikigdrive-prod:${{ github.sha }},wikigdrive-prod:latest"
push: false
- build-args: "{'--build-arg': 'GIT_SHA=${{ github.sha }}'}"
+ build-args: |
+ GIT_SHA=${{ github.sha }}
+ BUILD_UI=yes
+
+ - name: Build docs
+ run: |
+ docker run \
+ -v "/var/www/wikigdrive.com:/usr/src/app/website/.vitepress/dist" \
+ -e "GIT_SHA=${{ github.sha }}" \
+ "wikigdrive-prod:${{ github.sha }}" npm run npm run build --workspace website
- name: Stop and remove
run: docker stop wikigdrive-prod ; docker rm wikigdrive-prod
diff --git a/.github/workflows/feat-deploy.yml b/.github/workflows/feat-deploy.yml
index 94e20986..8ebba47a 100644
--- a/.github/workflows/feat-deploy.yml
+++ b/.github/workflows/feat-deploy.yml
@@ -28,7 +28,7 @@ jobs:
run: npm run lint
- name: Test
- run: npm run test
+ run: npm run test --workspaces --if-present
build:
needs: test
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index addbba36..12de2678 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -30,7 +30,7 @@ jobs:
run: npm run lint
- name: Test
- run: npm run test
+ run: npm run test --workspaces --if-present
build:
if: github.event.pull_request.head.ref != 'develop' && contains( github.event.pull_request.labels.*.name, 'deploy-pr')
@@ -49,31 +49,29 @@ jobs:
- name: Build action runner
run: docker build -t "wgd-action-runner:pr-${{ github.event.number }}" --build-arg "GIT_SHA=${{ github.sha }}" apps/wgd-action-runner
- - name: Build hugo docs
- run: |
- docker run \
- -v "${GITHUB_WORKSPACE}/hugo:/site" \
- -v "${GITHUB_WORKSPACE}/website:/website" \
- -v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/dist/hugo" \
- --env CONFIG_TOML="/site/config/_default/config.toml" --env BASE_URL="https://pr-${{ github.event.number }}.wikigdrive.com" \
- wgd-action-runner:pr-${{ github.event.number }} /steps/step_render_hugo
-
- - name: Copy index for vite
- run: mkdir -p ${GITHUB_WORKSPACE}/dist/hugo && cp -rf /var/www/pr-${{ github.event.number }}.wikigdrive.com/* ${GITHUB_WORKSPACE}/dist/hugo
-
- name: build
uses: docker/build-push-action@v6
with:
tags: "wikigdrive-feature:${{ github.sha }}"
push: false
- build-args: "{'--build-arg': 'GIT_SHA=${{ github.sha }}'}"
+ build-args: |
+ GIT_SHA=${{ github.sha }}
+ BUILD_UI=yes
+
+ - name: Build docs
+ run: |
+ docker run \
+ -v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/usr/src/app/website/.vitepress/dist" \
+ -e "GIT_SHA=${{ github.sha }}" \
+ "wikigdrive-feature:${{ github.sha }}" npm run build --workspace website
- name: Stop and remove
run: docker stop "pr-${{ github.event.number }}" ; docker rm "pr-${{ github.event.number }}"
continue-on-error: true
- name: "Create empty volume"
- run: docker volume rm -f "pr-${{ github.event.number }}" ; docker volume create "pr-${{ github.event.number }}"
+ run: docker volume create "pr-${{ github.event.number }}"
+ continue-on-error: true
- name: Start
run: |
@@ -83,15 +81,16 @@ jobs:
--tmpfs /tmp \
-v "pr-${{ github.event.number }}":/data \
-v /home/wikigdrive/service_account.json:/service_account.json \
- -v /home/wikigdrive/env.develop:/usr/src/app/.env \
+ -v /home/wikigdrive/env.pr:/usr/src/app/.env \
-v /var/run/docker.sock:/var/run/docker.sock \
- -v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/usr/src/app/dist/hugo" \
-e "GIT_SHA=${{ github.sha }}" \
+ -v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/usr/src/app/website/.vitepress/dist" \
-e "ZIPKIN_URL=https://pr-${{ github.event.number }}.wikigdrive.com/zipkin" \
-e "ZIPKIN_SERVICE=pr-${{ github.event.number }}" \
-e "AUTH_DOMAIN=https://dev.wikigdrive.com" \
-e "AUTH_INSTANCE=pr-${{ github.event.number }}" \
-e "DOMAIN=https://pr-${{ github.event.number }}.wikigdrive.com" \
+ -e "ACTION_IMAGE=wgd-action-runner:pr-${{ github.event.number }}" \
"wikigdrive-feature:${{ github.sha }}" wikigdrive \
--service_account /service_account.json \
--share_email mie-docs-wikigdrive@wikigdrive.iam.gserviceaccount.com \
diff --git a/.nvmrc b/.nvmrc
index d5a15960..ec7ba0e9 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20.10.0
+22.10.0
diff --git a/Dockerfile b/Dockerfile
index 2676da42..2f027c66 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,21 +1,29 @@
-FROM node:20-alpine
+FROM node:22-bookworm-slim
-RUN apk add --no-cache bash openssh-keygen git-lfs openssh-client
+ARG BUILD_UI
+ARG GIT_SHA
+EXPOSE 3000
+VOLUME /data
WORKDIR /usr/src/app
+RUN apt-get update
+RUN apt-get install -yq bash git-lfs openssh-client curl unzip socat
+RUN curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh
+
COPY package.json package-lock.json ./
-RUN npm install
-RUN npm install --location=global ts-node
+COPY deno.json deno.lock ./
+#RUN npm install
+#RUN npm install --location=global ts-node
COPY . ./
-RUN npm link --location=user
-EXPOSE 3000
-VOLUME /data
+RUN deno install
+#RUN if [ -z "$BUILD_UI" ] ; then cd /usr/src/app/apps/ui && npm run build ; fi
+#RUN npm link --location=user
+RUN ln -sf /usr/src/app/src/wikigdrive.sh /usr/local/bin/wikigdrive
+RUN ln -sf /usr/src/app/src/wikigdrivectl.sh /usr/local/bin/wikigdrivectl
-RUN cp /usr/src/app/hugo/themes/wgd-bootstrap/layouts/_default/baseof.html /usr/src/app/apps/ui/index.html
-RUN if [[ -d /usr/src/app/dist/hugo/ui ]]; then cp /usr/src/app/dist/hugo/ui/index.html /usr/src/app/apps/ui/index.html ; fi
-RUN cd /usr/src/app/apps/ui && npm install && npm run build
+RUN npm install && npm run build --workspaces
WORKDIR "/usr/src/app"
diff --git a/apps/ui/.eslintrc b/apps/ui/.eslintrc
deleted file mode 100644
index 3e2c5d20..00000000
--- a/apps/ui/.eslintrc
+++ /dev/null
@@ -1,39 +0,0 @@
-{
- "env": {
- "node": true,
- "commonjs": true,
- "es6": true,
- "mocha": true
- },
- "globals": {
- "Atomics": "readonly",
- "SharedArrayBuffer": "readonly"
- },
- "parser": "vue-eslint-parser",
- "parserOptions": { "parser": "@typescript-eslint/parser" },
- "plugins": [
- "@typescript-eslint"
- ],
- "extends": [
- "eslint:recommended",
- "plugin:vue/vue3-essential",
- "plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "rules": {
- "linebreak-style": [
- "error",
- "unix"
- ],
- "quotes": [
- "error",
- "single"
- ],
- "semi": [
- "error",
- "always"
- ],
- "@typescript-eslint/explicit-module-boundary-types": "off",
- "no-console": "off"
- }
-}
diff --git a/apps/ui/index.html b/apps/ui/index.html
new file mode 100644
index 00000000..a130bec7
--- /dev/null
+++ b/apps/ui/index.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+ WikiGDrive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/ui/package.json b/apps/ui/package.json
index 9fe352e5..6b2e65ef 100644
--- a/apps/ui/package.json
+++ b/apps/ui/package.json
@@ -12,17 +12,18 @@
},
"homepage": "https://github.com/mieweb/wikiGDrive#readme",
"scripts": {
- "build": "vite build"
+ "build": "vue-tsc -b && vite build"
},
"dependencies": {
- "vue": "3.5.12",
+ "bootstrap": "5.2.3",
+ "vue": "3.5.13",
"vue-prism-editor": "2.0.0-alpha.2",
"vue-router": "4.4.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.1.4",
- "eslint-plugin-vue": "8.7.1",
- "typescript": "4.9.4",
- "vite": "5.4.9"
+ "eslint-plugin-vue": "9.31.0",
+ "vite": "5.4.11",
+ "vue-tsc": "2.1.10"
}
}
diff --git a/hugo/static/images/logo.svg b/apps/ui/public/images/logo.svg
similarity index 100%
rename from hugo/static/images/logo.svg
rename to apps/ui/public/images/logo.svg
diff --git a/hugo/static/images/mieLogo.svg b/apps/ui/public/images/mieLogo.svg
similarity index 100%
rename from hugo/static/images/mieLogo.svg
rename to apps/ui/public/images/mieLogo.svg
diff --git a/apps/ui/src/App.vue b/apps/ui/src/App.vue
index 584c00ec..e55d3bf5 100644
--- a/apps/ui/src/App.vue
+++ b/apps/ui/src/App.vue
@@ -1,7 +1,7 @@
-
+
@@ -12,11 +12,34 @@ import ErrorView from './pages/ErrorView.vue';
export default {
data() {
- const el = document.querySelector('meta[name=errorMessage]');
+ let errorMessage = '';
+
+ if (!import.meta.env.SSR) {
+ const el = document.querySelector('meta[name=errorMessage]');
+ errorMessage = el ? el.getAttribute('content') : '';
+ }
+
return {
- errorMessage: el ? el.getAttribute('content') : ''
+ errorMessage
};
},
+ computed: {
+ drive() {
+ return this.$root.drive || {};
+ },
+ driveId() {
+ return this.drive.id;
+ },
+ isReady() {
+ if (!this.$route.meta?.requireDriveId) {
+ return true;
+ }
+ if (!this.driveId) {
+ return false;
+ }
+ return true;
+ }
+ },
components: {
ErrorView,
ModalsContainer, ToastsContainer
diff --git a/apps/ui/src/app.ts b/apps/ui/src/app.ts
new file mode 100644
index 00000000..b2ae9980
--- /dev/null
+++ b/apps/ui/src/app.ts
@@ -0,0 +1,255 @@
+import * as Vue from 'vue';
+import * as VueRouter from 'vue-router';
+import mitt from 'mitt';
+
+import App from './App.vue';
+import {ModalsMixin} from './modals/ModalsMixin';
+import {ToastsMixin} from './modals/ToastsMixin';
+import {AuthenticatedClient} from './services/AuthenticatedClient';
+import {DriveClientService} from './services/DriveClientService';
+import {GitClientService} from './services/GitClientService';
+import {SearchClientService} from './services/SearchClientService';
+import {CachedFileClientService} from './services/CachedFileClientService';
+import {markRaw} from 'vue';
+import AuthModal from './components/AuthModal.vue';
+
+function completedJob(job) {
+ return !['waiting', 'running'].includes(job.state);
+}
+
+const emitter = mitt();
+
+export function createApp() {
+ const app: Vue.App = Vue.createSSRApp({
+ components: {
+ 'App': App
+ },
+ mixins: [ModalsMixin, ToastsMixin],
+ data() {
+ return {
+ user: null,
+ drive: {},
+ jobs: [],
+ archive: [],
+ jobsMap: {},
+ changes: [],
+ changesMap: {}
+ };
+ },
+ template: '',
+ computed: {
+ gitStats() {
+ return Object.assign({
+ initialized: false,
+ remote_url: ''
+ }, this.drive.gitStats);
+ }
+ },
+ async created() {
+ this.authenticatedClient.app = this.$root;
+ this.emitter.on('*', async (type) => {
+ switch (type) {
+ case 'run_action:done':
+ case 'git_fetch:done':
+ case 'git_pull:done':
+ case 'git_push:done':
+ case 'git_reset:done':
+ case 'git_commit:done':
+ if (this.drive?.id) {
+ await this.changeDrive(this.drive.id);
+ }
+ this.emitter.emit('tree:changed');
+ break;
+ case 'tree:changed':
+ await this.FileClientService.clearCache();
+ break;
+ }
+ });
+ await this.fetchUser();
+ },
+ methods: {
+ async fetchUser() {
+ try {
+ const resUser = await authenticatedClient.fetchApi('/user/me');
+ const {user} = await resUser.json();
+ this.user = user;
+ } catch (err) {
+ this.user = null;
+ }
+ },
+ async changeDrive(toDriveId) {
+ try {
+ const vm = this.$root;
+
+ this.drive = await (vm).DriveClientService.changeDrive(toDriveId, vm);
+ if (!import.meta.env.SSR) {
+ const titleEl = document.querySelector('title');
+ if (titleEl) {
+ if (this.drive?.name) {
+ titleEl.innerText = this.drive?.name + ' - wikigdrive';
+ } else {
+ titleEl.innerText = 'wikigdrive';
+ }
+ }
+ }
+ } catch (err) {
+ this.drive = {
+ share_email: err.share_email,
+ id: toDriveId,
+ notRegistered: true
+ };
+ }
+ },
+ setJobs(jobs, archive) {
+ this.jobs = jobs;
+ this.archive = archive;
+ this.jobsMap = {};
+ for (const job of jobs) {
+ if (completedJob(job)) {
+ continue;
+ }
+
+ if (job.type === 'sync_all') {
+ this.jobsMap['sync_all'] = job;
+ continue;
+ }
+ if (job.type === 'transform') {
+ this.jobsMap['transform'] = job;
+ continue;
+ }
+ if (!job.payload) {
+ continue;
+ }
+ this.jobsMap[job.payload] = job;
+ }
+ },
+ setChanges(changes) {
+ this.changes = changes;
+ this.changesMap = {};
+ for (const change of changes) {
+ this.changesMap[change.id] = change;
+ }
+ }
+ },
+ errorCaptured(err) {
+ if (err['status'] === 401 || err['status'] === 403) {
+ const json = err['json'] || {};
+ if (json.authPath) {
+ this.$addModal({
+ component: markRaw(AuthModal),
+ props: {
+ authPath: json.authPath
+ },
+ });
+ return;
+ }
+ }
+ console.error('errorCaptured', Object.keys(err), err);
+ }
+ });
+
+ app.directive('grow', {
+ mounted(el, binding, vnode) {
+ el.rows = null;
+ el.style.resize = 'none';
+ el.style.minHeight = '50px';
+ setTimeout(() => {
+ el.style.height = (el.scrollHeight + 2)+'px';
+ }, 10);
+ setInterval(() => {
+ el.style.height = (el.scrollHeight + 2)+'px';
+ }, 1000);
+ },
+ updated() {
+ },
+ deep: true
+ });
+
+ const authenticatedClient = new AuthenticatedClient(null);
+
+ app.config.globalProperties.emitter = emitter;
+ app.config.globalProperties.authenticatedClient = authenticatedClient;
+ app.config.globalProperties.DriveClientService = new DriveClientService(authenticatedClient);
+ app.config.globalProperties.FileClientService = new CachedFileClientService(authenticatedClient);
+ app.config.globalProperties.GitClientService = new GitClientService(authenticatedClient);
+ app.config.globalProperties.SearchClientService = new SearchClientService(authenticatedClient);
+
+ let history = (import.meta.env.SSR) ? VueRouter.createMemoryHistory() : VueRouter.createWebHistory();
+
+ const router = VueRouter.createRouter({
+ history,
+ routes: [
+ {
+ path: '/drive/',
+ name: 'drives',
+ component: () => import('./pages/DrivesView.vue'),
+ },
+ {
+ path: '/drive/:driveId*',
+ name: 'drive',
+ meta: {
+ requireDriveId: true
+ },
+ component: () => import('./pages/FolderView.vue')
+ },
+ {
+ path: '/gdocs/:driveId/:fileId',
+ name: 'gdocs',
+ meta: {
+ requireDriveId: true
+ },
+ component: () => import('./pages/GDocsView.vue')
+ },
+ {
+ path: '/logs',
+ name: 'logs',
+ component: () => import('./pages/LogsView.vue')
+ },
+ {
+ path: '/share-drive/:driveId',
+ name: 'share-drive',
+ meta: {
+ requireDriveId: true
+ },
+ component: () => import('./pages/ShareView.vue'),
+ },
+ {
+ path: '/',
+ name: 'home',
+ meta: {
+ ssg: true
+ },
+ component: () => import('./pages/StaticView.vue')
+ },
+ {
+ path: '/docs',
+ name: 'docs',
+ meta: {
+ ssg: true
+ },
+ component: () => import('./pages/StaticView.vue')
+ },
+ {
+ path: '/docs/:pathMatch(.*)*',
+ name: 'docs',
+ meta: {
+ ssg: true
+ },
+ component: () => import('./pages/StaticView.vue')
+ },
+ {
+ path: '/:pathMatch(.*)*',
+ name: 'NotFound',
+ meta: {
+ ssg: true
+ },
+ component: () => import('./pages/StaticView.vue')
+ }
+ ]
+ });
+
+ app.use(router);
+
+ return {app, router};
+}
+
diff --git a/apps/ui/src/components/AuthModal.vue b/apps/ui/src/components/AuthModal.vue
index c2ea6813..651b6e8d 100644
--- a/apps/ui/src/components/AuthModal.vue
+++ b/apps/ui/src/components/AuthModal.vue
@@ -16,7 +16,7 @@
diff --git a/apps/ui/src/components/LogsViewer.vue b/apps/ui/src/components/LogsViewer.vue
index 08d3c0a4..2fe4474c 100644
--- a/apps/ui/src/components/LogsViewer.vue
+++ b/apps/ui/src/components/LogsViewer.vue
@@ -24,7 +24,7 @@
import {UtilsMixin} from './UtilsMixin.ts';
import StatusToolBar from './StatusToolBar.vue';
-import {Prism} from './prismFix';
+import {Prism} from './prismFix.ts';
export default {
mixins: [UtilsMixin],
diff --git a/apps/ui/src/components/MarkDown.vue b/apps/ui/src/components/MarkDown.vue
index e9beefdb..25276d93 100644
--- a/apps/ui/src/components/MarkDown.vue
+++ b/apps/ui/src/components/MarkDown.vue
@@ -2,7 +2,7 @@
diff --git a/apps/ui/src/components/SettingsTab.vue b/apps/ui/src/components/SettingsTab.vue
index c987eb17..728dccba 100644
--- a/apps/ui/src/components/SettingsTab.vue
+++ b/apps/ui/src/components/SettingsTab.vue
@@ -10,6 +10,10 @@
:tree-empty="treeEmpty"
@changed="fetch()"
/>
+
-
-
- 404 NOT FOUND
-
-
-
diff --git a/apps/ui/src/components/StatusToolBar.vue b/apps/ui/src/components/StatusToolBar.vue
index 2fb0cd20..94846cdf 100644
--- a/apps/ui/src/components/StatusToolBar.vue
+++ b/apps/ui/src/components/StatusToolBar.vue
@@ -40,7 +40,7 @@
/>
-
diff --git a/apps/ui/src/main.ts b/apps/ui/src/main.ts
index d5411210..07c950d6 100644
--- a/apps/ui/src/main.ts
+++ b/apps/ui/src/main.ts
@@ -1,262 +1,82 @@
-'use strict';
+import {Tooltip} from 'bootstrap';
+import {createApp} from './app.ts';
+import {addTelemetry} from './telemetry.ts';
-import * as Vue from 'vue';
-import * as VueRouter from 'vue-router';
-import mitt from 'mitt';
+const { app, router } = createApp();
+addTelemetry(app);
-import App from './App.vue';
-import {ModalsMixin} from './modals/ModalsMixin';
-import {ToastsMixin} from './modals/ToastsMixin';
-import {AuthenticatedClient} from './services/AuthenticatedClient';
-import {DriveClientService} from './services/DriveClientService';
-import {GitClientService} from './services/GitClientService';
-import {SearchClientService} from './services/SearchClientService';
-import {CachedFileClientService} from './services/CachedFileClientService';
-import {addTelemetry} from './telemetry';
-import {markRaw} from 'vue';
-import AuthModal from './components/AuthModal.vue';
-import {Tooltip} from 'bootstrap';
+app.mixin({
+ mounted() {
+ new Tooltip(document.body, {
+ selector: '[data-bs-toggle=tooltip]'
+ });
+ },
+});
-function completedJob(job) {
- return !['waiting', 'running'].includes(job.state);
+if (!import.meta.env.SSR) {
+ const appMountEl = document.getElementById('app');
+ if (appMountEl['__vue_app__'] && appMountEl['__vue_app__'].unmount) {
+ const vitePressApp = appMountEl['__vue_app__'];
+ const html = appMountEl.innerHTML;
+ vitePressApp.unmount();
+
+ appMountEl.innerHTML = html;
+ console.info('Unmounted vitepress app', vitePressApp);
+ }
}
-const emitter = mitt();
+router.isReady().then(async () => {
+ const vm = app.mount('#app', true);
-const initialHtmlContent = document.body.querySelector('.mainbar__content') ? document.body.querySelector('.mainbar__content').innerHTML : '';
+ await (vm as any).FileClientService.clearCache();
+ const driveIdParam = router.currentRoute?.value?.params?.driveId;
+ const driveId = Array.isArray(driveIdParam) ? driveIdParam[0] : driveIdParam;
+ if (driveId) {
+ await (vm as any).changeDrive(driveId);
+ }
-const app: Vue.App = Vue.createApp({
- components: {
- 'App': App
- },
- mixins: [ModalsMixin, ToastsMixin],
- data() {
- return {
- user: null,
- drive: {},
- jobs: [],
- archive: [],
- jobsMap: {},
- changes: [],
- changesMap: {},
- initialHtmlContent
- };
- },
- template: '',
- computed: {
- gitStats() {
- return Object.assign({
- initialized: false,
- remote_url: ''
- }, this.drive.gitStats);
- }
- },
- async created() {
- this.authenticatedClient.app = this.$root;
- this.emitter.on('*', async (type) => {
- switch (type) {
- case 'run_action:done':
- case 'git_fetch:done':
- case 'git_pull:done':
- case 'git_push:done':
- case 'git_reset:done':
- case 'git_commit:done':
- if (this.drive?.id) {
- await this.changeDrive(this.drive.id);
- }
- this.emitter.emit('tree:changed');
- break;
- case 'tree:changed':
- await this.FileClientService.clearCache();
- break;
- }
- });
- await this.fetchUser();
- },
- methods: {
- async fetchUser() {
- try {
- const resUser = await authenticatedClient.fetchApi('/user/me');
- const {user} = await resUser.json();
- this.user = user;
- } catch (err) {
- this.user = null;
- }
- },
- async changeDrive(toDriveId) {
+ router.beforeEach(async (to, from, next) => {
+ if (to.meta.ssg) {
try {
- this.drive = await (vm).DriveClientService.changeDrive(toDriveId, vm);
- const titleEl = document.querySelector('title');
- if (titleEl) {
- if (this.drive?.name) {
- titleEl.innerText = this.drive?.name + ' - wikigdrive';
- } else {
- titleEl.innerText = 'wikigdrive';
- }
+ const response = await fetch(window.location.protocol + '//' + window.location.host + to.path);
+ const html = await response.text();
+
+ const parser = new DOMParser();
+ const r = parser.parseFromString(html, 'text/html');
+ const titleElem = r.querySelector('title');
+ if (titleElem) {
+ document.title = titleElem.innerText;
}
- } catch (err) {
- this.drive = {
- share_email: err.share_email,
- id: toDriveId,
- notRegistered: true
- };
- }
- },
- setJobs(jobs, archive) {
- this.jobs = jobs;
- this.archive = archive;
- this.jobsMap = {};
- for (const job of jobs) {
- if (completedJob(job)) {
- continue;
- }
-
- if (job.type === 'sync_all') {
- this.jobsMap['sync_all'] = job;
- continue;
+ const mainContent = r.querySelector('.mainbar__content');
+ const elemContent = document.querySelector('.mainbar__content');
+ if (mainContent && elemContent) {
+ setTimeout(() => {
+ const emitter = (vm as any).emitter;
+ emitter.emit('html_lazy_content', mainContent.innerHTML);
+ }, 100);
}
- if (job.type === 'transform') {
- this.jobsMap['transform'] = job;
- continue;
- }
- if (!job.payload) {
- continue;
- }
- this.jobsMap[job.payload] = job;
- }
- },
- setChanges(changes) {
- this.changes = changes;
- this.changesMap = {};
- for (const change of changes) {
- this.changesMap[change.id] = change;
- }
- }
- },
- errorCaptured(err) {
- if (err['status'] === 401 || err['status'] === 403) {
- const json = err['json'] || {};
- if (json.authPath) {
- this.$addModal({
- component: markRaw(AuthModal),
- props: {
- authPath: json.authPath
- },
- });
+ next(true);
return;
+ } catch (err) {
+ console.error(err);
}
}
- console.error('errorCaptured', Object.keys(err), err);
- }
-});
-app.directive('grow', {
- mounted(el, binding, vnode) {
- el.rows = null;
- el.style.resize = 'none';
- el.style.minHeight = '50px';
- setTimeout(() => {
- el.style.height = (el.scrollHeight + 2)+'px';
- }, 10);
- setInterval(() => {
- el.style.height = (el.scrollHeight + 2)+'px';
- }, 1000);
- },
- updated() {
- console.log('updated');
- },
- deep: true
-});
-
-const authenticatedClient = new AuthenticatedClient(null);
-
-app.config.globalProperties.emitter = emitter;
-app.config.globalProperties.authenticatedClient = authenticatedClient;
-app.config.globalProperties.DriveClientService = new DriveClientService(authenticatedClient);
-app.config.globalProperties.FileClientService = new CachedFileClientService(authenticatedClient);
-app.config.globalProperties.GitClientService = new GitClientService(authenticatedClient);
-app.config.globalProperties.SearchClientService = new SearchClientService(authenticatedClient);
-
-const router = VueRouter.createRouter({
- history: VueRouter.createWebHistory(),
- routes: [
- {
- path: '/drive/',
- name: 'drives',
- component: () => import('./pages/DrivesView.vue'),
- },
- {
- path: '/drive/:driveId*',
- name: 'drive',
- component: () => import('./pages/FolderView.vue')
- },
- {
- path: '/gdocs/:driveId/:fileId',
- name: 'gdocs',
- component: () => import('./pages/GDocsView.vue')
- },
- {
- path: '/logs',
- name: 'logs',
- component: () => import('./pages/LogsView.vue')
- },
- {
- path: '/share-drive/:driveId',
- name: 'share-drive',
- component: () => import('./pages/ShareView.vue'),
- },
- {
- path: '/',
- name: 'home',
- component: () => import('./pages/StaticView.vue')
- },
- {
- path: '/docs',
- name: 'docs',
- component: () => import('./pages/StaticView.vue')
- },
- {
- path: '/docs/:pathMatch(.*)*',
- name: 'docs',
- component: () => import('./pages/StaticView.vue')
- },
- {
- path: '/:pathMatch(.*)*',
- name: 'NotFound',
- component: () => import('./pages/StaticView.vue')
+ const toDriveId = Array.isArray(to.params?.driveId) ? to.params.driveId[0] : to.params.driveId;
+ const fromDriveId = Array.isArray(from.params?.driveId) ? from.params.driveId[0] : from.params.driveId;
+ if (toDriveId !== fromDriveId) {
+ await (vm as any).FileClientService.clearCache();
+ await (vm as any).changeDrive(toDriveId);
}
- ]
-});
-
-app.use(router);
-
-app.mixin({
- mounted() {
- new Tooltip(document.body, {
- selector: '[data-bs-toggle=tooltip]'
+ next();
+ });
+ router.afterEach(() => {
+ const elements = document.querySelectorAll('[data-bs-toggle=tooltip]');
+ elements.forEach(element => {
+ const tooltip = Tooltip.getInstance(element);
+ if (tooltip) {
+ tooltip.hide();
+ }
});
- },
-});
-
-const vm = app.mount('#app');
-
-router.beforeEach(async (to, from, next) => {
- const toDriveId = Array.isArray(to.params?.driveId) ? to.params.driveId[0] : to.params.driveId;
- const fromDriveId = Array.isArray(from.params?.driveId) ? from.params.driveId[0] : from.params.driveId;
- if (toDriveId !== fromDriveId) {
- await (vm).FileClientService.clearCache();
- await (vm).changeDrive(toDriveId);
- }
- next();
-});
-router.afterEach(() => {
- const elements = document.querySelectorAll('[data-bs-toggle=tooltip]');
- elements.forEach(element => {
- const tooltip = Tooltip.getInstance(element);
- if (tooltip) {
- tooltip.hide();
- }
});
});
-
-addTelemetry(app);
diff --git a/apps/ui/src/modals/ToastsContainer.vue b/apps/ui/src/modals/ToastsContainer.vue
index 697e02ee..9886af76 100644
--- a/apps/ui/src/modals/ToastsContainer.vue
+++ b/apps/ui/src/modals/ToastsContainer.vue
@@ -3,12 +3,12 @@
+
+
+
+
+
+
diff --git a/apps/ui/src/pages/NotFound.vue b/apps/ui/src/pages/NotFound.vue
deleted file mode 100644
index 2b45aab5..00000000
--- a/apps/ui/src/pages/NotFound.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/apps/ui/src/pages/ShareView.vue b/apps/ui/src/pages/ShareView.vue
index 05814266..7b1a29c3 100644
--- a/apps/ui/src/pages/ShareView.vue
+++ b/apps/ui/src/pages/ShareView.vue
@@ -5,7 +5,7 @@
+