Skip to content

Commit

Permalink
Require that ASTRO_STUDIO_REMOTE_DB_URL is defined at runtime (#10533)
Browse files Browse the repository at this point in the history
* Require that ASTRO_STUDIO_REMOTE_DB_URL is defined at runtime

* Add changeset

* Fix build
  • Loading branch information
matthewp authored Mar 22, 2024
1 parent 8306ce1 commit 6576f5d
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-donkeys-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/db": patch
---

Ensure ASTRO_STUDIO_APP_TOKEN is found at runtime
1 change: 1 addition & 0 deletions packages/db/src/core/cli/commands/execute/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export async function cmd({
virtualModContents = getStudioVirtualModContents({
tables: dbConfig.tables ?? {},
appToken: appToken.token,
isBuild: false,
});
} else {
virtualModContents = getLocalVirtualModContents({
Expand Down
27 changes: 23 additions & 4 deletions packages/db/src/core/integration/vite-plugin-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ type VitePluginDBParams =

export function vitePluginDb(params: VitePluginDBParams): VitePlugin {
const srcDirPath = normalizePath(fileURLToPath(params.srcDir));
let command: 'build' | 'serve' = 'build';
return {
name: 'astro:db',
enforce: 'pre',
configResolved(resolvedConfig) {
command = resolvedConfig.command;
},
async resolveId(id, rawImporter) {
if (id !== VIRTUAL_MODULE_ID) return;
if (params.connectToStudio) return resolved.virtual;
Expand All @@ -61,6 +65,7 @@ export function vitePluginDb(params: VitePluginDBParams): VitePlugin {
return getStudioVirtualModContents({
appToken: params.appToken,
tables: params.tables.get(),
isBuild: command === 'build',
});
}
return getLocalVirtualModContents({
Expand Down Expand Up @@ -135,17 +140,31 @@ ${getStringifiedCollectionExports(tables)}`;
export function getStudioVirtualModContents({
tables,
appToken,
isBuild,
}: {
tables: DBTables;
appToken: string;
isBuild: boolean;
}) {
function appTokenArg() {
if(isBuild) {
// In production build, always read the runtime environment variable.
return 'process.env.ASTRO_STUDIO_APP_TOKEN';
} else {
return JSON.stringify(appToken);
}
}

function dbUrlArg() {
const dbStr = JSON.stringify(getRemoteDatabaseUrl());
// Allow overriding, mostly for testing
return `import.meta.env.ASTRO_STUDIO_REMOTE_DB_URL ?? ${dbStr}`;
}

return `
import {asDrizzleTable, createRemoteDatabaseClient} from ${RUNTIME_IMPORT};
export const db = await createRemoteDatabaseClient(process.env.ASTRO_STUDIO_APP_TOKEN ?? ${JSON.stringify(
appToken
// Respect runtime env for user overrides in SSR
)}, import.meta.env.ASTRO_STUDIO_REMOTE_DB_URL ?? ${JSON.stringify(getRemoteDatabaseUrl())});
export const db = await createRemoteDatabaseClient(${appTokenArg()}, ${dbUrlArg()});
export * from ${RUNTIME_CONFIG_IMPORT};
Expand Down
4 changes: 4 additions & 0 deletions packages/db/src/runtime/db-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const remoteResultSchema = z.object({
});

export function createRemoteDatabaseClient(appToken: string, remoteDbURL: string) {
if(appToken == null) {
throw new Error(`Cannot create a remote client: missing app token.`)
}

const url = new URL('/db/query', remoteDbURL);

const db = drizzleProxy(
Expand Down
25 changes: 25 additions & 0 deletions packages/db/test/basics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,29 @@ describe('astro:db', () => {
expect($('.username').text()).to.equal('Mario');
});
});

describe('build --remote', () => {
let remoteDbServer;

before(async () => {
process.env.ASTRO_STUDIO_APP_TOKEN = 'some token';
remoteDbServer = await setupRemoteDbServer(fixture.config);
await fixture.build();
});

after(async () => {
await remoteDbServer?.stop();
});

it('Can render page', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/');
const response = await app.render(request);
const html = await response.text();
const $ = cheerioLoad(html);

const ul = $('.authors-list');
expect(ul.children()).to.have.a.lengthOf(5);
});
});
});
10 changes: 10 additions & 0 deletions packages/db/test/fixtures/no-apptoken/astro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import db from '@astrojs/db';
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
integrations: [db()],
devToolbar: {
enabled: false,
},
});
14 changes: 14 additions & 0 deletions packages/db/test/fixtures/no-apptoken/db/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { column, defineDb, defineTable } from 'astro:db';


const User = defineTable({
columns: {
id: column.text({ primaryKey: true, optional: false }),
username: column.text({ optional: false, unique: true }),
password: column.text({ optional: false }),
},
});

export default defineDb({
tables: { User },
});
4 changes: 4 additions & 0 deletions packages/db/test/fixtures/no-apptoken/db/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

export default function() {

}
14 changes: 14 additions & 0 deletions packages/db/test/fixtures/no-apptoken/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@test/db-no-apptoken",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
},
"dependencies": {
"@astrojs/db": "workspace:*",
"astro": "workspace:*"
}
}
15 changes: 15 additions & 0 deletions packages/db/test/fixtures/no-apptoken/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
/// <reference path="../../.astro/db-types.d.ts" />
import { db, User } from 'astro:db';
// Just for the side-effect of running all the code
await db.select().from(User);
---
<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>Testing</h1>
</body>
</html>
36 changes: 36 additions & 0 deletions packages/db/test/ssr-no-apptoken.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect } from 'chai';
import testAdapter from '../../astro/test/test-adapter.js';
import { loadFixture } from '../../astro/test/test-utils.js';
import { setupRemoteDbServer } from './test-utils.js';

describe('missing app token', () => {
let fixture;
let remoteDbServer;
before(async () => {
fixture = await loadFixture({
root: new URL('./fixtures/no-apptoken/', import.meta.url),
output: 'server',
adapter: testAdapter(),
});

remoteDbServer = await setupRemoteDbServer(fixture.config);
await fixture.build();
// Ensure there's no token at runtime
delete process.env.ASTRO_STUDIO_APP_TOKEN;
});

after(async () => {
await remoteDbServer?.stop();
});

it('Errors as runtime', async () => {
const app = await fixture.loadTestAdapterApp();
const request = new Request('http://example.com/');
try {
const response = await app.render(request);
await response.text();
} catch {
expect(response.status).to.equal(501);
}
});
});
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

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

0 comments on commit 6576f5d

Please sign in to comment.