diff --git a/.env.example b/.env.example
index 460e575..76a74b6 100644
--- a/.env.example
+++ b/.env.example
@@ -1 +1 @@
-VITE_API_URL=http://localhost:8080
+VITE_API_URL=http://localhost:8090/api
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 8f083de..f6bbbc5 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -5,32 +5,30 @@ module.exports = {
browser: true,
es2021: true,
},
- extends: [
- 'eslint:recommended',
- 'plugin:vue/vue3-recommended',
- 'plugin:@typescript-eslint/recommended',
- 'prettier',
- ],
- parser: 'vue-eslint-parser',
+ extends: ["eslint:recommended", "plugin:vue/vue3-recommended", "plugin:@typescript-eslint/recommended", "prettier"],
+ parser: "vue-eslint-parser",
parserOptions: {
- ecmaVersion: 'latest',
- parser: '@typescript-eslint/parser',
- sourceType: 'module',
+ ecmaVersion: "latest",
+ parser: "@typescript-eslint/parser",
+ sourceType: "module",
},
- plugins: ['vue', '@typescript-eslint'],
+ plugins: ["vue", "@typescript-eslint"],
rules: {
- 'vue/multi-word-component-names': 'off',
- '@typescript-eslint/no-explicit-any': 'off',
- '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
- 'vue/max-attributes-per-line': 'off',
- 'vue/singleline-html-element-content-newline': 'off',
- 'vue/html-self-closing': ['warn', {
- html: { void: 'any', normal: 'always', component: 'always' },
- svg: 'always',
- math: 'always',
- }],
- 'vue/attributes-order': 'off',
- 'vue/require-default-prop': 'off',
- 'vue/use-v-on-exact': 'off',
+ "vue/multi-word-component-names": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
+ "vue/max-attributes-per-line": "off",
+ "vue/singleline-html-element-content-newline": "off",
+ "vue/html-self-closing": [
+ "warn",
+ {
+ html: { void: "any", normal: "always", component: "always" },
+ svg: "always",
+ math: "always",
+ },
+ ],
+ "vue/attributes-order": "off",
+ "vue/require-default-prop": "off",
+ "vue/use-v-on-exact": "off",
},
};
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3b95a3b..060fc67 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,8 +16,8 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
- cache: 'npm'
+ node-version: "20"
+ cache: "npm"
- name: Install dependencies
run: npm ci
@@ -37,8 +37,8 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
- cache: 'npm'
+ node-version: "20"
+ cache: "npm"
- name: Install dependencies
run: npm ci
@@ -55,8 +55,8 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
- cache: 'npm'
+ node-version: "20"
+ cache: "npm"
- name: Install dependencies
run: npm ci
@@ -74,8 +74,8 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
- node-version: '20'
- cache: 'npm'
+ node-version: "20"
+ cache: "npm"
- name: Install dependencies
run: npm ci
diff --git a/README.md b/README.md
index 553d36a..591694b 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ FlatRun UI provides a user-friendly dashboard for managing Docker deployments th
## Development
### Prerequisites
+
- Node.js 18+ and npm
### Setup
@@ -75,9 +76,11 @@ Create a `.env.local` file to configure the API connection:
```bash
# FlatRun Agent API URL
-VITE_API_URL=http://localhost:8090
+VITE_API_URL=http://localhost:8090/api
```
+`VITE_API_URL` must include the `/api` prefix used by the agent routes.
+
For production deployments:
```bash
@@ -101,8 +104,8 @@ If running the UI on a different domain/port than the agent, ensure the agent's
api:
enable_cors: true
allowed_origins:
- - http://localhost:5173 # Development
- - https://your-ui-domain.com # Production
+ - http://localhost:5173 # Development
+ - https://your-ui-domain.com # Production
```
## Available Scripts
@@ -160,16 +163,19 @@ EXPOSE 80
## Troubleshooting
### Cannot connect to agent
+
- Verify the agent is running: `systemctl status flatrun-agent`
- Check `VITE_API_URL` is correct in your `.env.local`
- Ensure CORS is configured if running on different domains
### Authentication fails
+
- Verify the API key matches the agent's configuration
- Check browser console for specific error messages
- Ensure JWT secret is properly set in agent config
### UI shows no deployments
+
- Confirm the agent's `deployments_path` is accessible
- Check that deployments have valid `docker-compose.yml` files
diff --git a/index.html b/index.html
index 058d4b8..8516593 100644
--- a/index.html
+++ b/index.html
@@ -1,9 +1,9 @@
-
+
-
-
-
+
+
+
Flatrun - Container Orchestration
diff --git a/package-lock.json b/package-lock.json
index 6bc5b9e..e30652b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
"primevue": "^3.50.0",
"vue": "^3.4.21",
"vue-codemirror": "^6.1.1",
+ "vue-i18n": "^9.14.5",
"vue-router": "^4.3.0"
},
"devDependencies": {
@@ -804,6 +805,47 @@
"dev": true,
"license": "BSD-3-Clause"
},
+ "node_modules/@intlify/core-base": {
+ "version": "9.14.5",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz",
+ "integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==",
+ "dependencies": {
+ "@intlify/message-compiler": "9.14.5",
+ "@intlify/shared": "9.14.5"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/message-compiler": {
+ "version": "9.14.5",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.14.5.tgz",
+ "integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==",
+ "dependencies": {
+ "@intlify/shared": "9.14.5",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/shared": {
+ "version": "9.14.5",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.14.5.tgz",
+ "integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -5182,6 +5224,26 @@
"eslint": ">=6.0.0"
}
},
+ "node_modules/vue-i18n": {
+ "version": "9.14.5",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
+ "integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==",
+ "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
+ "dependencies": {
+ "@intlify/core-base": "9.14.5",
+ "@intlify/shared": "9.14.5",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
"node_modules/vue-router": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz",
diff --git a/package.json b/package.json
index 005ce5c..d32ef7f 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"primevue": "^3.50.0",
"vue": "^3.4.21",
"vue-codemirror": "^6.1.1",
+ "vue-i18n": "^9.14.5",
"vue-router": "^4.3.0"
},
"devDependencies": {
diff --git a/public/flags/de.svg b/public/flags/de.svg
new file mode 100644
index 0000000..295d13e
--- /dev/null
+++ b/public/flags/de.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/flags/es.svg b/public/flags/es.svg
new file mode 100644
index 0000000..e143324
--- /dev/null
+++ b/public/flags/es.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/flags/fr.svg b/public/flags/fr.svg
new file mode 100644
index 0000000..ec311d7
--- /dev/null
+++ b/public/flags/fr.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/flags/gb.svg b/public/flags/gb.svg
new file mode 100644
index 0000000..91f11d8
--- /dev/null
+++ b/public/flags/gb.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/flags/it.svg b/public/flags/it.svg
new file mode 100644
index 0000000..be3a5df
--- /dev/null
+++ b/public/flags/it.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/flags/pt.svg b/public/flags/pt.svg
new file mode 100644
index 0000000..99b5fe6
--- /dev/null
+++ b/public/flags/pt.svg
@@ -0,0 +1,5 @@
+
diff --git a/src/components/BackupsTab.test.ts b/src/components/BackupsTab.test.ts
index 9dd2f09..87627b7 100644
--- a/src/components/BackupsTab.test.ts
+++ b/src/components/BackupsTab.test.ts
@@ -200,7 +200,7 @@ describe("BackupsTab", () => {
const statusBadge = wrapper.find(".backup-status");
expect(statusBadge.exists()).toBe(true);
- expect(statusBadge.text()).toBe("completed");
+ expect(statusBadge.text().toLowerCase()).toBe("completed");
});
it("displays component badges", async () => {
diff --git a/src/components/BackupsTab.vue b/src/components/BackupsTab.vue
index cd21c2e..2cc708c 100644
--- a/src/components/BackupsTab.vue
+++ b/src/components/BackupsTab.vue
@@ -1,28 +1,28 @@
- Loading backups...
+ {{ $t("deployment.backups.loading") }}
-
No backups yet
-
Create your first backup to protect your deployment data.
+
{{ $t("deployment.backups.noBackups") }}
+
{{ $t("deployment.backups.noBackupsDesc") }}
@@ -35,7 +35,7 @@
{{ formatBytes(backup.size) }}
{{ formatDate(backup.created_at) }}
- {{ backup.status }}
+ {{ formatBackupStatus(backup.status) }}
-
Scheduled Backups
+
{{ $t("deployment.backups.scheduledTitle") }}
{{ task.name }}
{{ task.cron_expr }}
- Next: {{ formatDate(task.next_run) }}
+
+ {{ $t("deployment.backups.nextRun") }} {{ formatDate(task.next_run) }}
+
@@ -149,10 +165,10 @@
@@ -160,10 +176,10 @@
@@ -171,10 +187,10 @@
@@ -183,11 +199,14 @@
+
+
diff --git a/src/components/LogViewer.vue b/src/components/LogViewer.vue
index a65026d..4e47fa6 100644
--- a/src/components/LogViewer.vue
+++ b/src/components/LogViewer.vue
@@ -7,20 +7,20 @@
{{ searchResults }}
-
@@ -30,21 +30,30 @@
-
+
-
+
-
+
-
+
-
+
@@ -52,19 +61,22 @@
-
{{ emptyMessage }}
+
{{ emptyMessage || $t("deployment.logViewer.empty") }}
diff --git a/src/components/database/DatabaseHeader.vue b/src/components/database/DatabaseHeader.vue
index d0d5776..e807801 100644
--- a/src/components/database/DatabaseHeader.vue
+++ b/src/components/database/DatabaseHeader.vue
@@ -3,14 +3,16 @@