diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
index 3220cf6b02..1e4dca48db 100644
--- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
+++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/API.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.api;
+import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Callable;
@@ -89,6 +90,13 @@ public class API {
private static final String STANDALONE_ERROR =
"GraphSpace management is not supported in standalone mode";
+ /**
+ * Shared date formatter for API response timestamps (thread-safe, reusable).
+ * Example output: {@code "2024-05-01 12:30:00"}
+ */
+ protected static final DateTimeFormatter DATE_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
public static HugeGraph graph(GraphManager manager, String graphSpace,
String graph) {
HugeGraph g = manager.graph(graphSpace, graph);
@@ -282,6 +290,28 @@ public static void validPermission(boolean hasPermission, String user,
}
}
+ /**
+ * Returns {@code true} if the profile's {@code "name"} or {@code "nickname"}
+ * field starts with the given prefix. Returns {@code true} when prefix is
+ * null or empty (i.e. no filtering).
+ *
+ *
Used by {@code GraphsAPI.listProfile} and {@code GraphSpaceAPI.listProfile}
+ * to filter results by a user-supplied prefix string.
+ *
+ * @param profile a map containing at least a {@code "name"} key
+ * @param prefix the prefix to match against; ignored when blank
+ * @return whether the entry matches the prefix filter
+ */
+ protected static boolean isPrefix(Map profile, String prefix) {
+ if (prefix == null || prefix.isEmpty()) {
+ return true;
+ }
+ String name = profile.get("name").toString();
+ Object nicknameObj = profile.get("nickname");
+ String nickname = nicknameObj != null ? nicknameObj.toString() : "";
+ return name.startsWith(prefix) || nickname.startsWith(prefix);
+ }
+
public static class ApiMeasurer {
public static final String EDGE_ITER = "edge_iterations";
diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/ManagerAPI.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/ManagerAPI.java
index 071e4b8a66..5e42190993 100644
--- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/ManagerAPI.java
+++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/ManagerAPI.java
@@ -22,9 +22,12 @@
import java.util.ArrayList;
import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+
import org.apache.hugegraph.api.API;
import org.apache.hugegraph.api.filter.StatusFilter;
import org.apache.hugegraph.auth.AuthManager;
+import org.apache.hugegraph.auth.HugeDefaultRole;
import org.apache.hugegraph.auth.HugeGraphAuthProxy;
import org.apache.hugegraph.auth.HugePermission;
import org.apache.hugegraph.core.GraphManager;
@@ -259,6 +262,46 @@ public String getRolesInGs(@Context GraphManager manager,
result));
}
+ @GET
+ @Timed
+ @Path("default")
+ @Consumes(APPLICATION_JSON)
+ public String checkDefaultRole(@Context GraphManager manager,
+ @QueryParam("graphspace") String graphSpace,
+ @QueryParam("role") String role,
+ @QueryParam("graph") String graph) {
+ LOG.debug("check if current user is default role: {} {} {}",
+ role, graphSpace, graph);
+ ensurePdModeEnabled(manager);
+ AuthManager authManager = manager.authManager();
+ String user = HugeGraphAuthProxy.username();
+
+ E.checkArgument(StringUtils.isNotEmpty(role) &&
+ StringUtils.isNotEmpty(graphSpace),
+ "Must pass graphspace and role params");
+
+ HugeDefaultRole defaultRole;
+ try {
+ defaultRole = HugeDefaultRole.valueOf(role.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ E.checkArgument(false, "Invalid role value '%s'", role);
+ defaultRole = null; // unreachable, satisfies compiler
+ }
+ boolean hasGraph = defaultRole.equals(HugeDefaultRole.OBSERVER);
+ E.checkArgument(!hasGraph || StringUtils.isNotEmpty(graph),
+ "Must set a graph for observer");
+
+ boolean result;
+ if (hasGraph) {
+ result = authManager.isDefaultRole(graphSpace, graph, user,
+ defaultRole);
+ } else {
+ result = authManager.isDefaultRole(graphSpace, user,
+ defaultRole);
+ }
+ return manager.serializer().writeMap(ImmutableMap.of("check", result));
+ }
+
private void validUser(AuthManager authManager, String user) {
E.checkArgument(authManager.findUser(user) != null ||
authManager.findGroup(user) != null,
@@ -274,7 +317,7 @@ private void validType(HugePermission type) {
private void validGraphSpace(GraphManager manager, String graphSpace) {
E.checkArgument(manager.graphSpace(graphSpace) != null,
- "The graph space is not exist");
+ "The graph space '%s' does not exist", graphSpace);
}
private static class JsonManager implements Checkable {
diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/UserAPI.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/UserAPI.java
index de51e6955d..c8b7c4af58 100644
--- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/UserAPI.java
+++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/auth/UserAPI.java
@@ -178,6 +178,9 @@ private static class JsonUser implements Checkable {
@JsonProperty("user_password")
@Schema(description = "The user password", required = true)
private String password;
+ @JsonProperty("user_nickname")
+ @Schema(description = "The user nickname")
+ private String nickname;
@JsonProperty("user_phone")
@Schema(description = "The user phone number")
private String phone;
@@ -197,6 +200,9 @@ public HugeUser build(HugeUser user) {
if (this.password != null) {
user.password(StringEncoding.hashPassword(this.password));
}
+ if (this.nickname != null) {
+ user.nickname(this.nickname);
+ }
if (this.phone != null) {
user.phone(this.phone);
}
@@ -215,6 +221,7 @@ public HugeUser build(HugeUser user) {
public HugeUser build() {
HugeUser user = new HugeUser(this.name);
user.password(StringEncoding.hashPassword(this.password));
+ user.nickname(this.nickname);
user.phone(this.phone);
user.email(this.email);
user.avatar(this.avatar);
@@ -233,10 +240,12 @@ public void checkCreate(boolean isBatch) {
@Override
public void checkUpdate() {
E.checkArgument(!StringUtils.isEmpty(this.password) ||
+ this.nickname != null ||
this.phone != null ||
this.email != null ||
- this.avatar != null,
- "Expect one of user password/phone/email/avatar]");
+ this.avatar != null ||
+ this.description != null,
+ "Expect one of user password/nickname/phone/email/avatar/description");
}
}
}
diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java
index 9316d7341b..d54f5af786 100644
--- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java
+++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/profile/GraphsAPI.java
@@ -18,16 +18,22 @@
package org.apache.hugegraph.api.profile;
import java.io.File;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-
import org.apache.commons.lang3.StringUtils;
import org.apache.hugegraph.HugeException;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.api.API;
import org.apache.hugegraph.api.filter.StatusFilter;
+import org.apache.hugegraph.auth.AuthManager;
import org.apache.hugegraph.auth.HugeAuthenticator.RequiredPerm;
import org.apache.hugegraph.auth.HugeGraphAuthProxy;
import org.apache.hugegraph.auth.HugePermission;
@@ -36,6 +42,7 @@
import org.apache.hugegraph.space.GraphSpace;
import org.apache.hugegraph.type.define.GraphMode;
import org.apache.hugegraph.type.define.GraphReadMode;
+import org.apache.hugegraph.util.ConfigUtil;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.JsonUtil;
import org.apache.hugegraph.util.Log;
@@ -74,6 +81,7 @@ public class GraphsAPI extends API {
private static final String CONFIRM_DROP = "I'm sure to drop the graph";
private static final String GRAPH_DESCRIPTION = "description";
private static final String GRAPH_ACTION = "action";
+ private static final String UPDATE = "update";
private static final String GRAPH_ACTION_RELOAD = "reload";
private static Map convConfig(Map config) {
@@ -120,6 +128,86 @@ public Object list(@Context GraphManager manager,
return ImmutableMap.of("graphs", filterGraphs);
}
+ @GET
+ @Timed
+ @Path("profile")
+ @Produces(APPLICATION_JSON_WITH_CHARSET)
+ @RolesAllowed({"space_member", "$dynamic"})
+ public Object listProfile(@Context GraphManager manager,
+ @Parameter(description = "The graph space name")
+ @PathParam("graphspace") String graphSpace,
+ @Parameter(description = "Filter graphs by name or nickname prefix")
+ @QueryParam("prefix") String prefix,
+ @Context SecurityContext sc) {
+ LOG.debug("List graph profiles in graph space {}", graphSpace);
+ if (null == manager.graphSpace(graphSpace)) {
+ throw new HugeException("Graphspace not exist!");
+ }
+ GraphSpace gs = manager.graphSpace(graphSpace);
+ // graphSpace.nickname() may be null in non-PD mode (GraphManager returns
+ // a placeholder GraphSpace without a nickname set)
+ String gsNickname = gs.nickname() != null ? gs.nickname() : graphSpace;
+
+ AuthManager authManager = manager.authManager();
+ String user = HugeGraphAuthProxy.username();
+ // Default graph concept relies on PD meta storage; in non-PD standalone
+ // mode there is no persistent store for this, so we gracefully degrade.
+ Map defaultGraphs;
+ if (manager.isPDEnabled()) {
+ defaultGraphs = authManager.getDefaultGraph(graphSpace, user);
+ } else {
+ defaultGraphs = new HashMap<>();
+ }
+
+ Set graphs = manager.graphs(graphSpace);
+ List