feat: add native Adminer database access
This commit is contained in:
@@ -12,7 +12,7 @@ Each signed runtime bundle contains a `runtime.json` manifest plus versioned exe
|
|||||||
|
|
||||||
Runtime archives are created from a staging directory with `npm run build:native-runtime -- <staging-directory> <output.tar.gz>`. The packager resolves every executable inside the staging root, calculates its SHA-256 checksum, writes the immutable `runtime.json`, excludes the build template, and creates the distributable archive. Dockside independently verifies those checksums before declaring a runtime available.
|
Runtime archives are created from a staging directory with `npm run build:native-runtime -- <staging-directory> <output.tar.gz>`. The packager resolves every executable inside the staging root, calculates its SHA-256 checksum, writes the immutable `runtime.json`, excludes the build template, and creates the distributable archive. Dockside independently verifies those checksums before declaring a runtime available.
|
||||||
|
|
||||||
The first reproducible bundle target is Linux x64. Run `npm run build:native-linux-x64` on a Docker-capable build machine. Docker is used only to create the portable artifact; users of that artifact do not need Docker. The recipe pins PHP 8.5.9, nginx 1.30.4, MariaDB 11.8.8, and WP-CLI 2.12.0 with their runtime libraries, runs executable, PHP-extension, and database-initialization smoke checks outside the build container, and then invokes the normal checksum packager.
|
The first reproducible bundle target is Linux x64. Run `npm run build:native-linux-x64` on a Docker-capable build machine. Docker is used only to create the portable artifact; users of that artifact do not need Docker. The recipe pins PHP 8.5.9, nginx 1.30.4, MariaDB 11.8.8, WP-CLI 2.12.0, and Adminer 6.0.1 with their runtime libraries, runs executable, PHP-extension, database-initialization, and PHP syntax smoke checks outside the build container, and then invokes the normal checksum packager.
|
||||||
|
|
||||||
Dockside can install a bundled runtime or a user-selected runtime archive from Settings. Installation rejects absolute and parent-traversing archive entries, rejects symbolic links, verifies the target platform and architecture, and verifies every declared executable checksum before atomically replacing an older runtime. Electron packages include matching archives from `dist/native-runtime` when they are present at packaging time.
|
Dockside can install a bundled runtime or a user-selected runtime archive from Settings. Installation rejects absolute and parent-traversing archive entries, rejects symbolic links, verifies the target platform and architecture, and verifies every declared executable checksum before atomically replacing an older runtime. Electron packages include matching archives from `dist/native-runtime` when they are present at packaging time.
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ Every project receives reserved loopback ports, generated service configuration,
|
|||||||
3. Linux x64 bundle with PHP 8.5, nginx, MariaDB 11.8, and WP-CLI.
|
3. Linux x64 bundle with PHP 8.5, nginx, MariaDB 11.8, and WP-CLI.
|
||||||
4. Native project lifecycle and single-site WordPress provisioning.
|
4. Native project lifecycle and single-site WordPress provisioning.
|
||||||
5. Native service logs, compressed snapshots, and database import/export.
|
5. Native service logs, compressed snapshots, and database import/export.
|
||||||
6. Native database administration.
|
6. Native database administration with loopback-only automatic login.
|
||||||
7. macOS arm64/x64 and Windows x64 bundles.
|
7. macOS arm64/x64 and Windows x64 bundles.
|
||||||
8. Additional PHP/database versions, Apache, Drupal, Node.js, and developer services.
|
8. Additional PHP/database versions, Apache, Drupal, Node.js, and developer services.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ RUN apk add --no-cache --repository=https://dl-cdn.alpinelinux.org/alpine/edge/m
|
|||||||
nginx=1.30.4-r3 mariadb=11.8.8-r0 mariadb-client=11.8.8-r0 bash file pax-utils \
|
nginx=1.30.4-r3 mariadb=11.8.8-r0 mariadb-client=11.8.8-r0 bash file pax-utils \
|
||||||
&& mkdir -p /stage/root /stage/bin /stage/lib \
|
&& mkdir -p /stage/root /stage/bin /stage/lib \
|
||||||
&& curl -fsSLo /tmp/wp-cli.phar https://github.com/wp-cli/wp-cli/releases/download/v2.12.0/wp-cli-2.12.0.phar \
|
&& curl -fsSLo /tmp/wp-cli.phar https://github.com/wp-cli/wp-cli/releases/download/v2.12.0/wp-cli-2.12.0.phar \
|
||||||
&& echo 'ce34ddd838f7351d6759068d09793f26755463b4a4610a5a5c0a97b68220d85c /tmp/wp-cli.phar' | sha256sum -c -
|
&& echo 'ce34ddd838f7351d6759068d09793f26755463b4a4610a5a5c0a97b68220d85c /tmp/wp-cli.phar' | sha256sum -c - \
|
||||||
|
&& curl -fsSLo /tmp/adminer.php https://github.com/vrana/adminer/releases/download/v6.0.1/adminer-6.0.1-mysql-en.php \
|
||||||
|
&& echo 'aa1964047a7ce6e44b26db14dc16a942820c54034b417e6223884df6fa8e24a9 /tmp/adminer.php' | sha256sum -c -
|
||||||
|
|
||||||
RUN docker-php-ext-install -j2 mysqli pdo_mysql
|
RUN docker-php-ext-install -j2 mysqli pdo_mysql
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ EOF
|
|||||||
done
|
done
|
||||||
|
|
||||||
cp /tmp/wp-cli.phar "$root/usr/local/bin/wp-cli.phar"
|
cp /tmp/wp-cli.phar "$root/usr/local/bin/wp-cli.phar"
|
||||||
|
mkdir -p "$root/usr/share/aurora"
|
||||||
|
cp /tmp/adminer.php "$root/usr/share/aurora/adminer.php"
|
||||||
cat > "$stage/bin/wp" <<'EOF'
|
cat > "$stage/bin/wp" <<'EOF'
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
runtime_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
runtime_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
|
||||||
@@ -136,6 +138,7 @@ cat > "$stage/runtime.template.json" <<EOF
|
|||||||
{ "id": "mariadb", "version": "$mariadb_version", "executable": "bin/mariadbd" },
|
{ "id": "mariadb", "version": "$mariadb_version", "executable": "bin/mariadbd" },
|
||||||
{ "id": "mariadb-client", "version": "$mariadb_version", "executable": "bin/mariadb" },
|
{ "id": "mariadb-client", "version": "$mariadb_version", "executable": "bin/mariadb" },
|
||||||
{ "id": "mariadb-dump", "version": "$mariadb_version", "executable": "bin/mariadb-dump" },
|
{ "id": "mariadb-dump", "version": "$mariadb_version", "executable": "bin/mariadb-dump" },
|
||||||
|
{ "id": "adminer", "version": "6.0.1", "executable": "root/usr/share/aurora/adminer.php" },
|
||||||
{ "id": "wp-cli", "version": "2.12.0", "executable": "bin/wp" }
|
{ "id": "wp-cli", "version": "2.12.0", "executable": "bin/wp" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,15 @@ if (extensions.error || extensions.status !== 0)
|
|||||||
throw extensions.error || new Error(`PHP database extensions failed: ${extensions.stderr}`)
|
throw extensions.error || new Error(`PHP database extensions failed: ${extensions.stderr}`)
|
||||||
process.stdout.write('PHP mysqli and pdo_mysql extensions OK\n')
|
process.stdout.write('PHP mysqli and pdo_mysql extensions OK\n')
|
||||||
|
|
||||||
|
const adminer = spawnSync(
|
||||||
|
join(root, 'bin/php'),
|
||||||
|
['-l', join(root, 'root/usr/share/aurora/adminer.php')],
|
||||||
|
{ encoding: 'utf8' }
|
||||||
|
)
|
||||||
|
if (adminer.error || adminer.status !== 0)
|
||||||
|
throw adminer.error || new Error(`Adminer PHP syntax check failed: ${adminer.stderr}`)
|
||||||
|
process.stdout.write('Adminer 6.0.1 PHP syntax OK\n')
|
||||||
|
|
||||||
const databaseDirectory = mkdtempSync(join(tmpdir(), 'aurora-native-mariadb-'))
|
const databaseDirectory = mkdtempSync(join(tmpdir(), 'aurora-native-mariadb-'))
|
||||||
const temporaryDirectory = mkdtempSync(join(tmpdir(), 'aurora-native-mariadb-tmp-'))
|
const temporaryDirectory = mkdtempSync(join(tmpdir(), 'aurora-native-mariadb-tmp-'))
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -116,8 +116,10 @@ async function assertNativeStack(
|
|||||||
throw new Error('Installed native runtime does not provide MariaDB 11.8.')
|
throw new Error('Installed native runtime does not provide MariaDB 11.8.')
|
||||||
if (!versions.has('nginx') || !versions.has('wp-cli'))
|
if (!versions.has('nginx') || !versions.has('wp-cli'))
|
||||||
throw new Error('Installed native runtime is missing nginx or WP-CLI.')
|
throw new Error('Installed native runtime is missing nginx or WP-CLI.')
|
||||||
if (stack.adminer !== false || stack.redis || stack.mailpit || stack.xdebug)
|
if (stack.adminer !== false && !versions.has('adminer'))
|
||||||
throw new Error('Adminer, Redis, Mailpit, and Xdebug are not enabled for Aurora Native yet.')
|
throw new Error('Installed native runtime does not provide Adminer.')
|
||||||
|
if (stack.redis || stack.mailpit || stack.xdebug)
|
||||||
|
throw new Error('Redis, Mailpit, and Xdebug are not enabled for Aurora Native yet.')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRegistry(): Promise<Registry> {
|
async function loadRegistry(): Promise<Registry> {
|
||||||
@@ -532,10 +534,11 @@ export async function describeProject(name: string): Promise<AuroraProjectDetail
|
|||||||
firefox_trust_status: c.runtimeEngine === 'native' ? 'unknown' : await firefoxTrustStatus(),
|
firefox_trust_status: c.runtimeEngine === 'native' ? 'unknown' : await firefoxTrustStatus(),
|
||||||
chromium_trust_status: c.runtimeEngine === 'native' ? 'unknown' : await chromiumTrustStatus(),
|
chromium_trust_status: c.runtimeEngine === 'native' ? 'unknown' : await chromiumTrustStatus(),
|
||||||
module_metadata: moduleMetadata,
|
module_metadata: moduleMetadata,
|
||||||
adminer_url:
|
adminer_url: c.modules.includes('adminer')
|
||||||
c.runtimeEngine === 'container' && c.modules.includes('adminer')
|
? c.runtimeEngine === 'native'
|
||||||
? `https://adminer.${projectHost(c.name)}`
|
? `${urlSet.http}/__aurora/adminer/`
|
||||||
: undefined,
|
: `https://adminer.${projectHost(c.name)}`
|
||||||
|
: undefined,
|
||||||
services,
|
services,
|
||||||
xdebug_enabled: c.xdebug === true,
|
xdebug_enabled: c.xdebug === true,
|
||||||
runtime_engine: c.runtimeEngine ?? 'container',
|
runtime_engine: c.runtimeEngine ?? 'container',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { promisify } from 'util'
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import {
|
import {
|
||||||
renderMariaDbConfig,
|
renderMariaDbConfig,
|
||||||
|
renderNativeAdminerBootstrap,
|
||||||
renderNginxConfig,
|
renderNginxConfig,
|
||||||
renderPhpFpmConfig,
|
renderPhpFpmConfig,
|
||||||
startNativeProject,
|
startNativeProject,
|
||||||
@@ -54,6 +55,16 @@ describe('native project configuration', () => {
|
|||||||
expect(config).toContain('port=41003')
|
expect(config).toContain('port=41003')
|
||||||
expect(config).toContain('/.aurora/native/data/mariadb')
|
expect(config).toContain('/.aurora/native/data/mariadb')
|
||||||
})
|
})
|
||||||
|
it('creates a loopback Adminer endpoint with project database credentials', () => {
|
||||||
|
expect(renderNginxConfig(project)).toContain('location = /__aurora/adminer/')
|
||||||
|
const bootstrap = renderNativeAdminerBootstrap({
|
||||||
|
...project,
|
||||||
|
installedRuntimeRoot: '/tmp/aurora-runtime'
|
||||||
|
})
|
||||||
|
expect(bootstrap).toContain("value = '127.0.0.1:41003'")
|
||||||
|
expect(bootstrap).toContain("username.value = 'db'")
|
||||||
|
expect(bootstrap).toContain('/tmp/aurora-runtime/root/usr/share/aurora/adminer.php')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it.runIf(Boolean(process.env.AURORA_NATIVE_SMOKE_ROOT))(
|
it.runIf(Boolean(process.env.AURORA_NATIVE_SMOKE_ROOT))(
|
||||||
@@ -170,6 +181,15 @@ it.runIf(Boolean(process.env.AURORA_NATIVE_WORDPRESS_SMOKE_ROOT))(
|
|||||||
{ cwd: root, env: process.env }
|
{ cwd: root, env: process.env }
|
||||||
)
|
)
|
||||||
expect(restored.stdout.trim()).toBe('Aurora native smoke')
|
expect(restored.stdout.trim()).toBe('Aurora native smoke')
|
||||||
|
|
||||||
|
const adminerResponse = await fetch(
|
||||||
|
`http://127.0.0.1:${definition.ports.http}/__aurora/adminer/`
|
||||||
|
)
|
||||||
|
const adminerHtml = await adminerResponse.text()
|
||||||
|
expect(adminerResponse.ok).toBe(true)
|
||||||
|
expect(adminerHtml).toContain('Adminer')
|
||||||
|
expect(adminerHtml).toContain("username.value = 'db'")
|
||||||
|
expect(adminerHtml).toContain("value = '127.0.0.1:")
|
||||||
} finally {
|
} finally {
|
||||||
await stopNativeProject(definition)
|
await stopNativeProject(definition)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,22 @@ http {
|
|||||||
server_name localhost;
|
server_name localhost;
|
||||||
root "${quoteNginx(webroot)}";
|
root "${quoteNginx(webroot)}";
|
||||||
index index.php index.html;
|
index index.php index.html;
|
||||||
|
location = /__aurora/adminer/ {
|
||||||
|
fastcgi_pass 127.0.0.1:${project.ports.php};
|
||||||
|
fastcgi_param SCRIPT_FILENAME "${quoteNginx(join(directory, 'adminer.php'))}";
|
||||||
|
fastcgi_param SCRIPT_NAME /__aurora/adminer/;
|
||||||
|
fastcgi_param REQUEST_METHOD $request_method;
|
||||||
|
fastcgi_param REQUEST_URI $request_uri;
|
||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
|
fastcgi_param CONTENT_TYPE $content_type;
|
||||||
|
fastcgi_param CONTENT_LENGTH $content_length;
|
||||||
|
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||||
|
fastcgi_param SERVER_NAME $server_name;
|
||||||
|
fastcgi_param SERVER_PORT $server_port;
|
||||||
|
fastcgi_param HTTP_HOST $http_host;
|
||||||
|
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||||
|
fastcgi_param REMOTE_PORT $remote_port;
|
||||||
|
}
|
||||||
location / { try_files $uri $uri/ /index.php?$query_string; }
|
location / { try_files $uri $uri/ /index.php?$query_string; }
|
||||||
location ~ \\.php$ {
|
location ~ \\.php$ {
|
||||||
fastcgi_pass 127.0.0.1:${project.ports.php};
|
fastcgi_pass 127.0.0.1:${project.ports.php};
|
||||||
@@ -92,6 +108,30 @@ http {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderNativeAdminerBootstrap(project: NativeProjectDefinition): string {
|
||||||
|
const adminer = join(installedRoot(project), 'root', 'usr', 'share', 'aurora', 'adminer.php')
|
||||||
|
return `<?php
|
||||||
|
// Generated by Aurora Core. This endpoint binds only to the project's loopback server.
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
addEventListener('DOMContentLoaded', () => {
|
||||||
|
const username = document.querySelector('input[name="auth[username]"]');
|
||||||
|
if (!username) return;
|
||||||
|
const form = username.form;
|
||||||
|
form.querySelector('input[name="auth[server]"]').value = '127.0.0.1:${project.ports.database}';
|
||||||
|
username.value = 'db';
|
||||||
|
form.querySelector('input[name="auth[password]"]').value = 'db';
|
||||||
|
form.querySelector('input[name="auth[db]"]').value = 'db';
|
||||||
|
const permanent = form.querySelector('input[name="auth[permanent]"]');
|
||||||
|
if (permanent) permanent.checked = true;
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
require '${adminer.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}';
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
export function renderMariaDbConfig(
|
export function renderMariaDbConfig(
|
||||||
project: NativeProjectDefinition,
|
project: NativeProjectDefinition,
|
||||||
installedRuntimeRoot = runtimeRoot()
|
installedRuntimeRoot = runtimeRoot()
|
||||||
@@ -128,6 +168,7 @@ export async function provisionNativeProject(project: NativeProjectDefinition):
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
writeFile(join(directory, 'config', 'php-fpm.conf'), renderPhpFpmConfig(project)),
|
writeFile(join(directory, 'config', 'php-fpm.conf'), renderPhpFpmConfig(project)),
|
||||||
writeFile(join(directory, 'config', 'nginx.conf'), renderNginxConfig(project)),
|
writeFile(join(directory, 'config', 'nginx.conf'), renderNginxConfig(project)),
|
||||||
|
writeFile(join(directory, 'adminer.php'), renderNativeAdminerBootstrap(project)),
|
||||||
writeFile(
|
writeFile(
|
||||||
join(directory, 'config', 'mariadb.cnf'),
|
join(directory, 'config', 'mariadb.cnf'),
|
||||||
renderMariaDbConfig(project, installedRoot(project))
|
renderMariaDbConfig(project, installedRoot(project))
|
||||||
|
|||||||
@@ -15,12 +15,15 @@ const componentIds = new Set([
|
|||||||
'nginx',
|
'nginx',
|
||||||
'apache',
|
'apache',
|
||||||
'mariadb',
|
'mariadb',
|
||||||
|
'mariadb-client',
|
||||||
|
'mariadb-dump',
|
||||||
'mysql',
|
'mysql',
|
||||||
'postgres',
|
'postgres',
|
||||||
'node',
|
'node',
|
||||||
'composer',
|
'composer',
|
||||||
'wp-cli',
|
'wp-cli',
|
||||||
'drush'
|
'drush',
|
||||||
|
'adminer'
|
||||||
])
|
])
|
||||||
|
|
||||||
export function validateNativeRuntimeManifest(value: unknown): AuroraNativeRuntimeManifest {
|
export function validateNativeRuntimeManifest(value: unknown): AuroraNativeRuntimeManifest {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import type {
|
|||||||
AuroraRuntimeUpdateStatus
|
AuroraRuntimeUpdateStatus
|
||||||
} from '../shared/types'
|
} from '../shared/types'
|
||||||
|
|
||||||
const catalogUrl = process.env.AURORA_RUNTIME_CATALOG_URL || 'https://auroradockside.com/runtime/catalog.json'
|
const catalogUrl =
|
||||||
|
process.env.AURORA_RUNTIME_CATALOG_URL || 'https://auroradockside.com/runtime/catalog.json'
|
||||||
const publicKeyPem = process.env.AURORA_RUNTIME_CATALOG_PUBLIC_KEY
|
const publicKeyPem = process.env.AURORA_RUNTIME_CATALOG_PUBLIC_KEY
|
||||||
|
|
||||||
function compareVersions(left: string, right: string): number {
|
function compareVersions(left: string, right: string): number {
|
||||||
@@ -25,13 +26,31 @@ function compareVersions(left: string, right: string): number {
|
|||||||
export function validateRuntimeCatalog(value: unknown): AuroraRuntimeCatalog {
|
export function validateRuntimeCatalog(value: unknown): AuroraRuntimeCatalog {
|
||||||
if (!value || typeof value !== 'object') throw new Error('Runtime catalog must be an object.')
|
if (!value || typeof value !== 'object') throw new Error('Runtime catalog must be an object.')
|
||||||
const catalog = value as Record<string, unknown>
|
const catalog = value as Record<string, unknown>
|
||||||
if (catalog.schema !== 1 || typeof catalog.generatedAt !== 'string' || !Array.isArray(catalog.releases))
|
if (
|
||||||
|
catalog.schema !== 1 ||
|
||||||
|
typeof catalog.generatedAt !== 'string' ||
|
||||||
|
!Array.isArray(catalog.releases)
|
||||||
|
)
|
||||||
throw new Error('Unsupported runtime catalog.')
|
throw new Error('Unsupported runtime catalog.')
|
||||||
const releases = catalog.releases.map((entry) => {
|
const releases = catalog.releases.map((entry) => {
|
||||||
if (!entry || typeof entry !== 'object') throw new Error('Invalid runtime catalog release.')
|
if (!entry || typeof entry !== 'object') throw new Error('Invalid runtime catalog release.')
|
||||||
const release = entry as Record<string, unknown>
|
const release = entry as Record<string, unknown>
|
||||||
if (
|
if (
|
||||||
!['php', 'nginx', 'apache', 'mariadb', 'mysql', 'postgres', 'node', 'composer', 'wp-cli', 'drush'].includes(String(release.component)) ||
|
![
|
||||||
|
'php',
|
||||||
|
'nginx',
|
||||||
|
'apache',
|
||||||
|
'mariadb',
|
||||||
|
'mariadb-client',
|
||||||
|
'mariadb-dump',
|
||||||
|
'mysql',
|
||||||
|
'postgres',
|
||||||
|
'node',
|
||||||
|
'composer',
|
||||||
|
'wp-cli',
|
||||||
|
'drush',
|
||||||
|
'adminer'
|
||||||
|
].includes(String(release.component)) ||
|
||||||
typeof release.version !== 'string' ||
|
typeof release.version !== 'string' ||
|
||||||
!['linux', 'darwin', 'win32'].includes(String(release.platform)) ||
|
!['linux', 'darwin', 'win32'].includes(String(release.platform)) ||
|
||||||
!['x64', 'arm64'].includes(String(release.arch)) ||
|
!['x64', 'arm64'].includes(String(release.arch)) ||
|
||||||
@@ -40,7 +59,8 @@ export function validateRuntimeCatalog(value: unknown): AuroraRuntimeCatalog {
|
|||||||
!release.url.startsWith('https://') ||
|
!release.url.startsWith('https://') ||
|
||||||
typeof release.sha256 !== 'string' ||
|
typeof release.sha256 !== 'string' ||
|
||||||
!/^[a-f0-9]{64}$/i.test(release.sha256)
|
!/^[a-f0-9]{64}$/i.test(release.sha256)
|
||||||
) throw new Error('Invalid runtime catalog release.')
|
)
|
||||||
|
throw new Error('Invalid runtime catalog release.')
|
||||||
return release as unknown as AuroraRuntimeCatalogRelease
|
return release as unknown as AuroraRuntimeCatalogRelease
|
||||||
})
|
})
|
||||||
return { schema: 1, generatedAt: catalog.generatedAt, releases }
|
return { schema: 1, generatedAt: catalog.generatedAt, releases }
|
||||||
@@ -53,12 +73,20 @@ export function findRuntimeUpdates(
|
|||||||
arch: string
|
arch: string
|
||||||
): AuroraRuntimeUpdate[] {
|
): AuroraRuntimeUpdate[] {
|
||||||
return installed.flatMap((component) => {
|
return installed.flatMap((component) => {
|
||||||
const candidates = catalog.releases.filter((release) =>
|
const candidates = catalog.releases.filter(
|
||||||
release.component === component.id && release.platform === platform && release.arch === arch
|
(release) =>
|
||||||
|
release.component === component.id && release.platform === platform && release.arch === arch
|
||||||
)
|
)
|
||||||
const latest = candidates.sort((a, b) => compareVersions(b.version, a.version))[0]
|
const latest = candidates.sort((a, b) => compareVersions(b.version, a.version))[0]
|
||||||
return latest && compareVersions(latest.version, component.version) > 0
|
return latest && compareVersions(latest.version, component.version) > 0
|
||||||
? [{ component: component.id, installedVersion: component.version, availableVersion: latest.version, channel: latest.channel }]
|
? [
|
||||||
|
{
|
||||||
|
component: component.id,
|
||||||
|
installedVersion: component.version,
|
||||||
|
availableVersion: latest.version,
|
||||||
|
channel: latest.channel
|
||||||
|
}
|
||||||
|
]
|
||||||
: []
|
: []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -76,7 +104,8 @@ async function remoteCatalog(): Promise<AuroraRuntimeCatalog> {
|
|||||||
fetch(catalogUrl, { signal: controller.signal }),
|
fetch(catalogUrl, { signal: controller.signal }),
|
||||||
fetch(`${catalogUrl}.sig`, { signal: controller.signal })
|
fetch(`${catalogUrl}.sig`, { signal: controller.signal })
|
||||||
])
|
])
|
||||||
if (!catalogResponse.ok || !signatureResponse.ok) throw new Error('Runtime catalog server is unavailable.')
|
if (!catalogResponse.ok || !signatureResponse.ok)
|
||||||
|
throw new Error('Runtime catalog server is unavailable.')
|
||||||
const body = Buffer.from(await catalogResponse.arrayBuffer())
|
const body = Buffer.from(await catalogResponse.arrayBuffer())
|
||||||
const signature = Buffer.from((await signatureResponse.text()).trim(), 'base64')
|
const signature = Buffer.from((await signatureResponse.text()).trim(), 'base64')
|
||||||
if (!verify(null, body, createPublicKey(publicKeyPem), signature))
|
if (!verify(null, body, createPublicKey(publicKeyPem), signature))
|
||||||
@@ -87,7 +116,9 @@ async function remoteCatalog(): Promise<AuroraRuntimeCatalog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRuntimeUpdates(installed: AuroraNativeRuntimeComponent[]): Promise<AuroraRuntimeUpdateStatus> {
|
export async function getRuntimeUpdates(
|
||||||
|
installed: AuroraNativeRuntimeComponent[]
|
||||||
|
): Promise<AuroraRuntimeUpdateStatus> {
|
||||||
const cacheDirectory = join(app.getPath('userData'), 'runtime-catalog')
|
const cacheDirectory = join(app.getPath('userData'), 'runtime-catalog')
|
||||||
const cachePath = join(cacheDirectory, 'catalog.json')
|
const cachePath = join(cacheDirectory, 'catalog.json')
|
||||||
let source: AuroraRuntimeUpdateStatus['source'] = 'bundled'
|
let source: AuroraRuntimeUpdateStatus['source'] = 'bundled'
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }): React.
|
|||||||
setWebServer('nginx')
|
setWebServer('nginx')
|
||||||
setDatabase('mariadb')
|
setDatabase('mariadb')
|
||||||
setDatabaseVersion('11.8')
|
setDatabaseVersion('11.8')
|
||||||
setAdminer(false)
|
setAdminer(true)
|
||||||
setRedis(false)
|
setRedis(false)
|
||||||
setMailpit(false)
|
setMailpit(false)
|
||||||
setXdebug(false)
|
setXdebug(false)
|
||||||
@@ -459,7 +459,7 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }): React.
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
disabled={runtimeEngine === 'native'}
|
disabled={runtimeEngine === 'native' && label !== 'Adminer'}
|
||||||
checked={value as boolean}
|
checked={value as boolean}
|
||||||
onChange={(e) => (setter as (v: boolean) => void)(e.target.checked)}
|
onChange={(e) => (setter as (v: boolean) => void)(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+30
-5
@@ -2,7 +2,20 @@ export type ProjectStatus = 'running' | 'stopped' | 'paused' | 'starting' | 'sto
|
|||||||
export type AuroraRuntimeEngine = 'container' | 'native'
|
export type AuroraRuntimeEngine = 'container' | 'native'
|
||||||
|
|
||||||
export interface AuroraNativeRuntimeComponent {
|
export interface AuroraNativeRuntimeComponent {
|
||||||
id: 'php' | 'nginx' | 'apache' | 'mariadb' | 'mysql' | 'postgres' | 'node' | 'composer' | 'wp-cli' | 'drush'
|
id:
|
||||||
|
| 'php'
|
||||||
|
| 'nginx'
|
||||||
|
| 'apache'
|
||||||
|
| 'mariadb'
|
||||||
|
| 'mariadb-client'
|
||||||
|
| 'mariadb-dump'
|
||||||
|
| 'mysql'
|
||||||
|
| 'postgres'
|
||||||
|
| 'node'
|
||||||
|
| 'composer'
|
||||||
|
| 'wp-cli'
|
||||||
|
| 'drush'
|
||||||
|
| 'adminer'
|
||||||
version: string
|
version: string
|
||||||
executable: string
|
executable: string
|
||||||
sha256: string
|
sha256: string
|
||||||
@@ -18,8 +31,20 @@ export interface AuroraNativeRuntimeManifest {
|
|||||||
|
|
||||||
export interface AuroraRuntimeStatus {
|
export interface AuroraRuntimeStatus {
|
||||||
selectedEngine: AuroraRuntimeEngine
|
selectedEngine: AuroraRuntimeEngine
|
||||||
container: { available: boolean; provider: 'docker' | 'podman' | null; version?: string; reason?: string }
|
container: {
|
||||||
native: { available: boolean; platform: string; arch: string; runtimeVersion?: string; components: AuroraNativeRuntimeComponent[]; reason?: string }
|
available: boolean
|
||||||
|
provider: 'docker' | 'podman' | null
|
||||||
|
version?: string
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
native: {
|
||||||
|
available: boolean
|
||||||
|
platform: string
|
||||||
|
arch: string
|
||||||
|
runtimeVersion?: string
|
||||||
|
components: AuroraNativeRuntimeComponent[]
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuroraRuntimeComponentId = AuroraNativeRuntimeComponent['id']
|
export type AuroraRuntimeComponentId = AuroraNativeRuntimeComponent['id']
|
||||||
@@ -189,7 +214,6 @@ export interface AuroraSnapshot {
|
|||||||
Created: string
|
Created: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type AuroraModuleCategory = 'application' | 'service' | 'tool'
|
export type AuroraModuleCategory = 'application' | 'service' | 'tool'
|
||||||
export type AuroraModuleSettingType = 'boolean' | 'select' | 'text' | 'number'
|
export type AuroraModuleSettingType = 'boolean' | 'select' | 'text' | 'number'
|
||||||
|
|
||||||
@@ -252,7 +276,8 @@ export interface AuroraModuleManifest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuroraModuleLifecycleHook = 'projectCreate' | 'projectStart' | 'projectRemove' | 'packageUninstall' | 'projectTool'
|
export type AuroraModuleLifecycleHook =
|
||||||
|
'projectCreate' | 'projectStart' | 'projectRemove' | 'packageUninstall' | 'projectTool'
|
||||||
|
|
||||||
export interface AuroraModuleCommand {
|
export interface AuroraModuleCommand {
|
||||||
command: string
|
command: string
|
||||||
|
|||||||
Reference in New Issue
Block a user