Skip to content

Commit 75fc901

Browse files
authored
Merge pull request #1231 from lowcoder-org/feature-private-repo
Enabled private repo api in api-service and fixed response type issue.
2 parents 66b527b + a839beb commit 75fc901

File tree

8 files changed

+77
-39
lines changed

8 files changed

+77
-39
lines changed

client/packages/lowcoder/src/comps/comps/remoteComp/loaders.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@ import {
88
} from "types/remoteComp";
99

1010
async function npmLoader(
11-
remoteInfo: RemoteCompInfo
11+
{
12+
appId,
13+
...remoteInfo
14+
}: RemoteCompInfo & {appId?: string}
1215
): Promise<CompConstructor | null> {
1316

1417
// Falk: removed "packageVersion = "latest" as default value fir packageVersion - to ensure no automatic version jumping.
1518
const localPackageVersion = remoteInfo.packageVersion || "latest";
1619
const { packageName, packageVersion, compName } = remoteInfo;
17-
const entry = `${NPM_PLUGIN_ASSETS_BASE_URL}/${packageName}@${localPackageVersion}/index.js`;
20+
const entry = `${NPM_PLUGIN_ASSETS_BASE_URL}/${appId}/${packageName}@${localPackageVersion}/index.js`;
1821

1922
try {
2023
const module = await import(
@@ -51,7 +54,7 @@ async function bundleLoader(
5154
return comp;
5255
}
5356

54-
export const loaders: Record<RemoteCompSource, RemoteCompLoader> = {
57+
export const loaders: Record<RemoteCompSource, RemoteCompLoader<RemoteCompInfo & {appId?: string}>> = {
5558
npm: npmLoader,
5659
bundle: bundleLoader,
5760
};

client/packages/lowcoder/src/comps/comps/remoteComp/remoteComp.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { CompContext } from "@lowcoder-ee/comps/utils/compContext";
1616
import React from "react";
1717
import type { AppState } from "@lowcoder-ee/redux/reducers";
1818
import { useSelector } from "react-redux";
19+
import { useApplicationId } from "@lowcoder-ee/util/hooks";
1920

2021
const ViewError = styled.div`
2122
display: flex;
@@ -51,7 +52,7 @@ interface RemoteCompReadyAction {
5152

5253
interface RemoteCompViewProps {
5354
isLowcoderComp?: boolean;
54-
loadComp: (packageVersion?: string) => Promise<void>;
55+
loadComp: (packageVersion?: string, appId?: string) => Promise<void>;
5556
loadingElement?: () => React.ReactNode;
5657
errorElement?: (error: any) => React.ReactNode;
5758
source?: RemoteCompSource;
@@ -62,6 +63,7 @@ const RemoteCompView = React.memo((props: React.PropsWithChildren<RemoteCompView
6263
const [error, setError] = useState<any>("");
6364
const editorState = useContext(EditorContext);
6465
const compState = useContext(CompContext);
66+
const appId = useApplicationId();
6567
const lowcoderCompPackageVersion = editorState?.getAppSettings().lowcoderCompVersion || 'latest';
6668
const latestLowcoderCompsVersion = useSelector((state: AppState) => state.npmPlugin.packageVersion['lowcoder-comps']);
6769

@@ -79,7 +81,7 @@ const RemoteCompView = React.memo((props: React.PropsWithChildren<RemoteCompView
7981

8082
useMount(() => {
8183
setError("");
82-
loadComp(packageVersion).catch((e) => {
84+
loadComp(packageVersion, appId).catch((e) => {
8385
setError(String(e));
8486
});
8587
});
@@ -117,7 +119,7 @@ export function remoteComp<T extends RemoteCompInfo = RemoteCompInfo>(
117119
this.compValue = params.value;
118120
}
119121

120-
private async load(packageVersion = 'latest') {
122+
private async load(packageVersion = 'latest', appId = 'none') {
121123
if (!remoteInfo) {
122124
return;
123125
}
@@ -129,7 +131,7 @@ export function remoteComp<T extends RemoteCompInfo = RemoteCompInfo>(
129131
log.error("loader not found, remote info:", remoteInfo);
130132
return;
131133
}
132-
const RemoteExportedComp = await finalLoader({...remoteInfo, packageVersion});
134+
const RemoteExportedComp = await finalLoader({...remoteInfo, packageVersion, appId});
133135
if (!RemoteExportedComp) {
134136
return;
135137
}
@@ -159,7 +161,7 @@ export function remoteComp<T extends RemoteCompInfo = RemoteCompInfo>(
159161
<RemoteCompView
160162
key={key}
161163
isLowcoderComp={remoteInfo?.packageName === 'lowcoder-comps'}
162-
loadComp={(packageVersion?: string) => this.load(packageVersion)}
164+
loadComp={(packageVersion?: string, appId?: string) => this.load(packageVersion, appId)}
163165
loadingElement={loadingElement}
164166
source={remoteInfo?.source}
165167
/>

client/packages/lowcoder/src/comps/utils/remote.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function parseCompType(compType: string) {
3939
}
4040

4141
export async function getNpmPackageMeta(packageName: string) {
42-
const res = await axios.get<NpmPackageMeta>(`${NPM_REGISTRY_URL}/${packageName}`);
42+
const res = await axios.get<NpmPackageMeta>(`${NPM_REGISTRY_URL}/none/${packageName}`);
4343
if (res.status >= 400) {
4444
return null;
4545
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1-
export const SERVER_HOST = `${REACT_APP_NODE_SERVICE_URL ?? ""}`;
2-
export const NPM_REGISTRY_URL = `${SERVER_HOST}/node-service/api/npm/registry`;
3-
export const NPM_PLUGIN_ASSETS_BASE_URL = `${SERVER_HOST}/node-service/api/npm/package`;
1+
// export const SERVER_HOST = `${REACT_APP_NODE_SERVICE_URL ?? ""}`;
2+
// export const NPM_REGISTRY_URL = `${SERVER_HOST}/node-service/api/npm/registry`;
3+
// export const NPM_PLUGIN_ASSETS_BASE_URL = `${SERVER_HOST}/node-service/api/npm/package`;
4+
5+
export const SERVER_HOST = `${REACT_APP_API_SERVICE_URL ?? ""}`;
6+
export const NPM_REGISTRY_URL = `${SERVER_HOST}/api/npm/registry`;
7+
export const NPM_PLUGIN_ASSETS_BASE_URL = `${SERVER_HOST}/api/npm/package`;

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/security/SecurityConfig.java

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
145145
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.ORGANIZATION_URL + "/*/datasourceTypes"), // datasource types
146146
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.ORGANIZATION_URL + "/byuser/*"),
147147
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.DATASOURCE_URL + "/jsDatasourcePlugins"),
148+
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, NewUrl.NPM_REGISTRY + "/**"),
148149
ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/api/docs/**")
149150
)
150151
.permitAll()

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/npm/PrivateNpmRegistryController.java

+48-20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import lombok.RequiredArgsConstructor;
44
import org.jetbrains.annotations.NotNull;
55
import org.lowcoder.api.home.SessionUserService;
6+
import org.lowcoder.domain.application.service.ApplicationServiceImpl;
7+
import org.lowcoder.domain.organization.service.OrgMemberServiceImpl;
68
import org.lowcoder.domain.organization.service.OrganizationService;
79
import org.lowcoder.infra.constant.NewUrl;
810
import org.lowcoder.infra.js.NodeServerHelper;
@@ -27,33 +29,59 @@ public class PrivateNpmRegistryController implements PrivateNpmRegistryEndpoint{
2729

2830
private static final String NPM_REGISTRY_METADATA = "npm/registry";
2931
private static final String NPM_REGISTRY_ASSET = "npm/package";
32+
private final OrgMemberServiceImpl orgMemberServiceImpl;
33+
private final ApplicationServiceImpl applicationServiceImpl;
3034

3135
@Override
32-
public Mono<ResponseEntity<Resource>> getNpmPackageMeta(String name) {
33-
return forwardToNodeService(name, NPM_REGISTRY_METADATA);
36+
public Mono<ResponseEntity<Resource>> getNpmPackageMeta(String applicationId, String name) {
37+
return forwardToNodeService(applicationId, name, NPM_REGISTRY_METADATA);
3438
}
3539

3640
@Override
37-
public Mono<ResponseEntity<Resource>> getNpmPackageAsset(String path) {
38-
return forwardToNodeService(path, NPM_REGISTRY_ASSET);
41+
public Mono<ResponseEntity<Resource>> getNpmPackageAsset(String applicationId, String path) {
42+
return forwardToNodeService(applicationId, path, NPM_REGISTRY_ASSET);
3943
}
4044

4145
@NotNull
42-
private Mono<ResponseEntity<Resource>> forwardToNodeService(String path, String prefix) {
43-
return sessionUserService.getVisitorOrgMemberCache().flatMap(orgMember -> organizationService.getOrgCommonSettings(orgMember.getOrgId()).flatMap(organizationCommonSettings -> {
44-
Map<String, Object> config = Map.of("npmRegistries", organizationCommonSettings.get("npmRegistries"), "workspaceId", orgMember.getOrgId());
45-
return WebClientBuildHelper.builder()
46-
.systemProxy()
47-
.build()
48-
.post()
49-
.uri(nodeServerHelper.createUri(prefix + "/" + path))
50-
.contentType(MediaType.APPLICATION_JSON)
51-
.body(BodyInserters.fromValue(config))
52-
.retrieve().toEntity(Resource.class)
53-
.map(response -> ResponseEntity
54-
.status(response.getStatusCode())
55-
.headers(response.getHeaders())
56-
.body(response.getBody()));
57-
}));
46+
private Mono<ResponseEntity<Resource>> forwardToNodeService(String applicationId, String path, String prefix) {
47+
48+
String withoutLeadingSlash = path.startsWith("/") ? path.substring(1) : path;
49+
if(applicationId.equals("none")) {
50+
return sessionUserService.getVisitorOrgMemberCache().flatMap(orgMember -> organizationService.getOrgCommonSettings(orgMember.getOrgId()).flatMap(organizationCommonSettings -> {
51+
Map<String, Object> config = Map.of("npmRegistries", organizationCommonSettings.get("npmRegistries"), "workspaceId", orgMember.getOrgId());
52+
return WebClientBuildHelper.builder()
53+
.systemProxy()
54+
.build()
55+
.post()
56+
.uri(nodeServerHelper.createUri(prefix + "/" + withoutLeadingSlash))
57+
.contentType(MediaType.APPLICATION_JSON)
58+
.body(BodyInserters.fromValue(config))
59+
.retrieve().toEntity(Resource.class)
60+
.map(response -> {
61+
return ResponseEntity
62+
.status(response.getStatusCode())
63+
.headers(response.getHeaders())
64+
.body(response.getBody());
65+
});
66+
}));
67+
} else{
68+
return applicationServiceImpl.findById(applicationId).flatMap(application -> organizationService.getById(application.getOrganizationId())).flatMap(orgMember -> organizationService.getOrgCommonSettings(orgMember.getId()).flatMap(organizationCommonSettings -> {
69+
Map<String, Object> config = Map.of("npmRegistries", organizationCommonSettings.get("npmRegistries"), "workspaceId", orgMember.getId());
70+
return WebClientBuildHelper.builder()
71+
.systemProxy()
72+
.build()
73+
.post()
74+
.uri(nodeServerHelper.createUri(prefix + "/" + withoutLeadingSlash))
75+
.contentType(MediaType.APPLICATION_JSON)
76+
.body(BodyInserters.fromValue(config))
77+
.retrieve().toEntity(Resource.class)
78+
.map(response -> {
79+
return ResponseEntity
80+
.status(response.getStatusCode())
81+
.headers(response.getHeaders())
82+
.body(response.getBody());
83+
});
84+
}));
85+
}
5886
}
5987
}

server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/npm/PrivateNpmRegistryEndpoint.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ public interface PrivateNpmRegistryEndpoint {
2121
summary = "Get NPM registry Metadata",
2222
description = "Retrieve the metadata of private NPM registry package."
2323
)
24-
// @GetMapping("/registry/{name}")
25-
public Mono<ResponseEntity<Resource>> getNpmPackageMeta(@PathVariable String name);
24+
@GetMapping("/registry/{applicationId}/{name}")
25+
public Mono<ResponseEntity<Resource>> getNpmPackageMeta(@PathVariable String applicationId, @PathVariable String name);
2626

2727
@Operation(
2828
tags = TAG_NPM_REGISTRY_MANAGEMENT,
2929
operationId = "getNpmPackageAsset",
3030
summary = "Get NPM registry asset",
3131
description = "Retrieve the asset of private NPM registry package."
3232
)
33-
// @GetMapping("/package/{path}")
34-
public Mono<ResponseEntity<Resource>> getNpmPackageAsset(@PathVariable String path);
33+
@GetMapping("/package/{applicationId}/{*path}")
34+
public Mono<ResponseEntity<Resource>> getNpmPackageAsset(@PathVariable String applicationId, @PathVariable String path);
3535
}

server/node-service/src/controllers/npm.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from "fs/promises";
33
import { spawn } from "child_process";
44
import { Request as ServerRequest, Response as ServerResponse } from "express";
55
import { NpmRegistryService, NpmRegistryConfigEntry, NpmRegistryConfig } from "../services/npmRegistry";
6-
6+
import {default as pathlib} from 'path';
77

88
type RequestConfig = {
99
workspaceId: string;
@@ -217,7 +217,7 @@ async function fetchPackageFileInner(request: ServerRequest, response: ServerRes
217217
logger.info(`Fetching tarball: ${tarball}`);
218218
await fetchAndUnpackTarball(tarball, packageId, packageVersion, registry, baseDir);
219219
} catch (error) {
220-
logger.error(`Error fetching package: ${error} ${(error as {stack: string}).stack}`);
220+
logger.error(`Error fetching package: ${error} ${(error as any)?.stack}`);
221221
return response.status(500).send("Internal server error");
222222
} finally {
223223
PackageProcessingQueue.resolve(packageId);
@@ -232,7 +232,7 @@ async function fetchPackageFileInner(request: ServerRequest, response: ServerRes
232232
return response.sendFile(`${packageBaseDir}/index.mjs`);
233233
}
234234

235-
return response.sendFile(`${packageBaseDir}/${file}`);
235+
return response.sendFile(pathlib.resolve(`${packageBaseDir}/${file}`));
236236
} catch (error) {
237237
logger.error(`Error fetching package file: ${error} ${(error as {stack: string})?.stack?.toString()}`);
238238
response.status(500).send("Internal server error");

0 commit comments

Comments
 (0)