Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
- `base44 deploy` (and `base44 site deploy`) can now build first: interactive runs ask, and `--build` / `--no-build` pre-answer the prompt.
- `base44 dev --remote` serves the frontend against the production backend: it runs `site.serveCommand` with `VITE_BASE44_APP_ID` and `VITE_BASE44_APP_BASE_URL` pointing at the app's own published URL, without starting the local backend. Fails if the app has no published URL.

### Changed

- The `backend-and-client` template now scaffolds the same client convention editor-created apps use: `@base44/vite-plugin` + `src/lib/app-params.js`, with the SDK client on same-origin `/api` (`serverUrl: ''`). Under `base44 dev` the plugin proxies `/api` to the local dev backend, so scaffolded apps get local entities and functions; the app id is injected via `VITE_BASE44_APP_ID` by `base44 dev`, `base44 dev --remote`, and the build/deploy commands instead of being baked into source.

### Fixed

- `base44 link` now lists editor-created apps; previously only apps created by the CLI could be linked.
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/cli/commands/project/scaffold-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ export async function completeProjectSetup(
await execa({ cwd: resolvedPath, shell: true })`${installCommand}`;

updateMessage("Building project...");
await execa({ cwd: resolvedPath, shell: true })`${buildCommand}`;
await execa({
cwd: resolvedPath,
shell: true,
env: { VITE_BASE44_APP_ID: projectId },
})`${buildCommand}`;

updateMessage("Deploying site...");
return await deploySite(join(resolvedPath, outputDirectory));
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/core/site/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export async function deploySite(
`Output directory does not exist: ${siteOutputDir}. Make sure to build your project first.`,
{
hints: [
{ message: "Run your build command (e.g., 'npm run build') first" },
{
message:
"Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)",
},
],
},
);
Expand All @@ -29,7 +32,10 @@ export async function deploySite(
siteOutputDir,
{
hints: [
{ message: "Run your build command (e.g., 'npm run build') first" },
{
message:
"Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)",
},
],
},
);
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/templates/backend-and-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"preview": "vite preview"
},
"dependencies": {
"@base44/sdk": "^0.8.3",
"@base44/sdk": "^0.8.40",
"@base44/vite-plugin": "^1.0.30",
"lucide-react": "^0.475.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/templates/backend-and-client/src/api/base44Client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createClient } from '@base44/sdk';
import { appParams } from '@/lib/app-params';

const { appId, token, functionsVersion, appBaseUrl } = appParams;

export const base44 = createClient({
appId,
token,
functionsVersion,
serverUrl: '',
appBaseUrl
});

This file was deleted.

28 changes: 28 additions & 0 deletions packages/cli/templates/backend-and-client/src/lib/app-params.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getAccessToken } from '@base44/sdk';

const isNode = typeof window === 'undefined';

const isClearAccessTokenRequested = () =>
!isNode && new URLSearchParams(window.location.search).get("clear_access_token") === 'true';

const clearStoredAccessToken = () => {
window.localStorage.removeItem('base44_access_token');
window.localStorage.removeItem('token');
}

const getAppParams = () => {
if (isClearAccessTokenRequested()) {
clearStoredAccessToken();
}
return {
appId: import.meta.env.VITE_BASE44_APP_ID,
token: getAccessToken(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont need this here, the SDK handles it internally, and I think we'd rather keep it there

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same in app params PR in apper

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, though looks like something else breaks:
https://github.com/base44-dev/apper/blob/2479b59dd6a85bdadd7a2d6a922b7149563f9c2f/templates/apps_template/src/lib/AuthContext.jsx#L33-L42 -> seems to bypass the sdk and break if app-params doesn't return the token, am I missing something?

https://github.com/base44-dev/apper/blob/2479b59dd6a85bdadd7a2d6a922b7149563f9c2f/backend/app/user_apps/auth_templates/files/OAuthConsent.jsx#L38 -> same, raw fetch (also L89)

we hit this on the apper side - dropped the storage fallback and in-iframe reloads rendered signed out while sdk calls kept working.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, yeah we need to tackle those, they are very problematic.
for the first one - it does

import { createAxiosClient } from '@base44/sdk/dist/utils/axios-client';

which is very bad by itself as that path is not formal api and we will break it unknowingly

this whole call should just be in the SDK itself in the frist place, so the template code doesnt try to read the access token and isnt aware of api paths. so let's introduce a new method for it in the SDK.

(more over I dont know why we still have a call to public_settings in the template since we moved to protecting private apps on the server. @roymiloh why do we still call it?)

for the second one - I think we can get the token from an initialized client nope? dont we have a method for that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@netanelgilad went on vacations. @guyofeck and I decided to merge this as is and leave further improvements for way down the line

functionsVersion: import.meta.env.VITE_BASE44_FUNCTIONS_VERSION,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one should be read from query parameter and not env var (is there a code path where we inject this as env var? I think we only ever place it as a query parameter, take a look in apper code)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's the other way around
https://github.com/base44-dev/apper/blob/2479b59dd6a85bdadd7a2d6a922b7149563f9c2f/backend/app/user_apps/sandbox/providers/sandbox_provider.py#L1076-L1078 sets the env var. I can't find anyplace setting functions_version queryParam, I don't think there is one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@netanelgilad went on vacations. @guyofeck and I decided to merge this as is and leave further improvements for way down the line

appBaseUrl: import.meta.env.VITE_BASE44_APP_BASE_URL,
}
}


export const appParams = {
...getAppParams()
}
11 changes: 3 additions & 8 deletions packages/cli/templates/backend-and-client/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { defineConfig } from 'vite';
import base44 from '@base44/vite-plugin';
import react from '@vitejs/plugin-react';
import path from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
plugins: [base44(), react()],
});
46 changes: 46 additions & 0 deletions packages/cli/tests/cli/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,52 @@ describe("create command", () => {
expect(config).toContain('"visibility": "public"');
});

it("scaffolds backend-and-client with the editor-app client convention", async () => {
await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });
t.api.mockCreateApp({ id: "convention-app-id", name: "Convention App" });

const projectPath = join(t.getTempDir(), "convention-app");

const result = await t.run(
"create",
"Convention App",
"--path",
projectPath,
"--template",
"backend-and-client",
"--no-skills",
);

t.expectResult(result).toSucceed();

const client = await readFile(
join(projectPath, "src", "api", "base44Client.js"),
"utf-8",
);
expect(client).toContain("serverUrl: ''");
expect(client).toContain("@/lib/app-params");
expect(client).not.toContain("convention-app-id");

const appParams = await readFile(
join(projectPath, "src", "lib", "app-params.js"),
"utf-8",
);
expect(appParams).toContain("VITE_BASE44_APP_ID");
expect(appParams).toContain("VITE_BASE44_APP_BASE_URL");

const viteConfig = await readFile(
join(projectPath, "vite.config.js"),
"utf-8",
);
expect(viteConfig).toContain("@base44/vite-plugin");

const appConfig = await readFile(
join(projectPath, "base44", ".app.jsonc"),
"utf-8",
);
expect(appConfig).toContain("convention-app-id");
});

it("infers path from name when --path is not provided", async () => {
await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });
t.api.mockCreateApp({ id: "inferred-path-id", name: "My App" });
Expand Down