graphxr-database-proxy 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- graphxr_database_proxy/__init__.py +16 -0
- graphxr_database_proxy/api/__init__.py +1 -0
- graphxr_database_proxy/api/database.py +266 -0
- graphxr_database_proxy/api/google.py +437 -0
- graphxr_database_proxy/api/projects.py +99 -0
- graphxr_database_proxy/common/util.py +38 -0
- graphxr_database_proxy/drivers/__init__.py +1 -0
- graphxr_database_proxy/drivers/base.py +56 -0
- graphxr_database_proxy/drivers/factory.py +36 -0
- graphxr_database_proxy/drivers/spanner.py +814 -0
- graphxr_database_proxy/main.py +110 -0
- graphxr_database_proxy/models/__init__.py +1 -0
- graphxr_database_proxy/models/google.py +50 -0
- graphxr_database_proxy/models/project.py +170 -0
- graphxr_database_proxy/proxy.py +495 -0
- graphxr_database_proxy/proxyForDev.py +290 -0
- graphxr_database_proxy/services/__init__.py +1 -0
- graphxr_database_proxy/services/project_service.py +150 -0
- graphxr_database_proxy/static/favicon.ico +0 -0
- graphxr_database_proxy/static/index.html +1 -0
- graphxr_database_proxy/static/main.7391ee9773c403483393.css +175 -0
- graphxr_database_proxy/static/main.7391ee9773c403483393.css.map +1 -0
- graphxr_database_proxy/static/main.ce3fbb85a7bc9452edb9.js +2 -0
- graphxr_database_proxy/static/main.ce3fbb85a7bc9452edb9.js.map +1 -0
- graphxr_database_proxy/static/vendors.70542a99f336a8021013.js +3 -0
- graphxr_database_proxy/static/vendors.70542a99f336a8021013.js.LICENSE.txt +95 -0
- graphxr_database_proxy/static/vendors.70542a99f336a8021013.js.map +1 -0
- graphxr_database_proxy-1.0.0.dist-info/METADATA +180 -0
- graphxr_database_proxy-1.0.0.dist-info/RECORD +33 -0
- graphxr_database_proxy-1.0.0.dist-info/WHEEL +5 -0
- graphxr_database_proxy-1.0.0.dist-info/entry_points.txt +2 -0
- graphxr_database_proxy-1.0.0.dist-info/licenses/LICENSE +21 -0
- graphxr_database_proxy-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(()=>{"use strict";var e,t,a,r={2465:(e,t,a)=>{var r=a(6540),n=a(5338),o=a(4976),l=a(867),c=a(7767),i=a(5448),s=a(1847),u=a(2702),d=a(7206),m=a(1468),p=a(6191),y=a(6704),g=a(9237),h=a(88),f=a(3740);const{Header:E}=i.A,{Title:A}=s.A,_=()=>{const e=(0,c.Zp)(),t=(0,c.zy)(),a=[{key:"/",icon:r.createElement(p.A,null),label:"Home"},{key:"/listProjects",icon:r.createElement(y.A,null),label:"Projects"},{key:"/addProject",icon:r.createElement(g.A,null),label:"Add Project"}];return r.createElement(E,{className:"app-header"},r.createElement("div",{className:"header-content"},r.createElement(u.A,{align:"center"},r.createElement(h.A,{className:"logo-icon"}),r.createElement(A,{level:3,className:"title",style:{color:"white",margin:0}},"GraphXR Database Proxy")),r.createElement(d.A,{theme:"dark",mode:"horizontal",selectedKeys:[t.pathname],items:a,onClick:t=>{e(t.key)},style:{flex:1,justifyContent:"end"}}),r.createElement(u.A,null,r.createElement(m.Ay,{type:"text",icon:r.createElement(f.A,null),onClick:()=>{window.open("/docs","_blank")},className:"header-button"},"API Docs"))))};var v=a(3532),b=a(6914),j=a(2977),w=a(9763),P=a(6395),S=a(4314),I=a(6349),O=a(7046),C=a(385),k=a(261),x=a(2629),T=a(3598);const D=a(1083).A.create({baseURL:"",timeout:3e4,headers:{"Content-Type":"application/json"}});D.interceptors.request.use(e=>e,e=>Promise.reject(e)),D.interceptors.response.use(e=>e,e=>(console.error("API Error:",e),Promise.reject(e)));const L=new class{async getProjects(){return(await D.get("/api/project/list")).data}async getProject(e){return(await D.get("/api/project/".concat(e))).data}async createProject(e){return(await D.post("/api/project/create",e)).data}async updateProject(e,t){return(await D.put("/api/project/update?project_id=".concat(e),t)).data}async deleteProject(e){await D.delete("/api/project/delete?project_id=".concat(e))}async getDatabaseInfo(e,t){return(await D.get("/api/".concat(e,"/").concat(t))).data}async executeQuery(e,t,a){return(await D.post("/api/".concat(e,"/").concat(t,"/query"),a)).data}async getSchema(e,t){return(await D.get("/api/".concat(e,"/").concat(t,"/schema"))).data}async getGraphSchema(e,t){return(await D.get("/api/".concat(e,"/").concat(t,"/graphSchema"))).data}async getSampleData(e,t){return(await D.get("/api/".concat(e,"/").concat(t,"/sampleData"))).data}async testConnection(e,t){return(await D.post("/api/".concat(e,"/").concat(t,"/test"))).data}};var F=a(3221),q=a(3691),N=a(7152),R=a(6370),z=a(9957),U=a(6337),G=a(7028);const B=["name","database_type"];function V(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),a.push.apply(a,r)}return a}function M(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?V(Object(a),!0).forEach(function(t){K(e,t,a[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):V(Object(a)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))})}return e}function K(e,t,a){return(t=function(e){var t=function(e){if("object"!=typeof e||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var a=t.call(e,"string");if("object"!=typeof a)return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:t+""}(t))in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}const{Option:J}=F.A,X="http://localhost:9080"===window.location.origin?"oauth2":"service_account",H=e=>{var t,a;let{initialValues:n,onSubmit:o,onCancel:l}=e;const[c]=q.A.useForm(),[i,s]=r.useState("spanner"),[d,p]=r.useState(X),[y,g]=r.useState({}),[h,f]=r.useState({listProjects:!1,listDatabases:!1}),[E,A]=r.useState([]),[_,b]=r.useState([]);(0,r.useEffect)(()=>{n&&(c.setFieldsValue(M({name:n.name,database_type:n.database_type},n.database_config)),s(n.database_type),p(n.database_config.auth_type),j(M({},n.database_config.oauth_config)))},[n,c]),(0,r.useEffect)(()=>{const e=y&&(null==y?void 0:y.token)&&"oauth2"==d,t=y&&"service_account"===(null==y?void 0:y.type)&&y.private_key_id;(e||t)&&(async()=>{try{f({listDatabases:!1,listProjects:!0});const e=await fetch("/api/google/spanner/list_projects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({auth:y})});if(e.ok){const t=await e.json();A(t)}else v.Ay.error("Failed to fetch projects");f({listDatabases:!1,listProjects:!1})}catch(e){v.Ay.error("Error fetching projects")}})()},[null==y?void 0:y.token,d,null==y?void 0:y.type,y.private_key_id]),(0,r.useEffect)(()=>{y&&null!=y&&y.project_id&&null!=y&&y.instance_id&&(async()=>{try{f({listDatabases:!0,listProjects:!1});const e=await fetch("/api/google/spanner/list_databases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({auth:y})});if(e.ok){const t=await e.json();b(t)}else v.Ay.error("Failed to fetch databases");f({listDatabases:!1,listProjects:!1})}catch(e){v.Ay.error("Error fetching databases")}})()},[null==y?void 0:y.project_id,null==y?void 0:y.instance_id]);const j=e=>{const t=M(M({},y),e);g(t=>M(M({},t),e)),c.setFieldsValue({project_id:t.project_id,instance_id:t.instance_id,database_id:t.database_id,graph_name:t.graph_name})};return r.createElement(q.A,{form:c,layout:"vertical",onFinish:e=>{const{name:t,database_type:a}=e,r=function(e,t){if(null==e)return{};var a,r,n=function(e,t){if(null==e)return{};var a={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(-1!==t.indexOf(r))continue;a[r]=e[r]}return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)a=o[r],-1===t.indexOf(a)&&{}.propertyIsEnumerable.call(e,a)&&(n[a]=e[a])}return n}(e,B);let n={type:a,auth_type:d,options:{}};if("spanner"===a)if(n=M(M({},n),{},{project_id:r.project_id,instance_id:r.instance_id,database_id:r.database_id,graph_name:r.graph_name||""}),"oauth2"===d)n.oauth_config=M({},y);else if("service_account"===d){if(!y)return void v.Ay.error("Please upload a service account key file");n.project_id=null==y?void 0:y.project_id,n.oauth_config=M({},y)}o({name:t,database_type:a,database_config:n})},initialValues:{redirect_uri:"http://localhost:9080/google/spanner/callback",max_sessions:100,timeout:30,read_only:!0,auth_type:d,database_type:"spanner"}},r.createElement(N.A,{gutter:16},r.createElement(R.A,{span:24},r.createElement(q.A.Item,{label:"Project Name",name:"name",rules:[{required:!0,message:"Please enter project name"}]},r.createElement(z.A,{placeholder:"Enter project name"})))),r.createElement(N.A,{gutter:16},r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Database Type",name:"database_type",rules:[{required:!0,message:"Please select database type"}]},r.createElement(F.A,{placeholder:"Select database type",onChange:e=>{s(e),c.resetFields(["project_id","instance_id","database_id","host","port"])}},r.createElement(J,{value:"spanner"},"Google Cloud Spanner")))),r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Authentication Type",name:"auth_type",rules:[{required:!0,message:"Please select authentication type"}]},r.createElement(F.A,{placeholder:"Select authentication type",onChange:e=>{p(e),c.resetFields(["client_id","client_secret","username","password"]),j({})}},r.createElement(J,{value:"oauth2"},"OAuth2"),r.createElement(J,{value:"service_account"},"Service Account"))))),"spanner"===i&&r.createElement(P.A,{title:"Spanner Configuration",size:"small",style:{marginBottom:16}},"oauth2"===d&&r.createElement(P.A,{title:"OAuth2 Configuration",size:"small",type:"inner"},r.createElement(N.A,{gutter:16},(null==y?void 0:y.email)&&r.createElement(R.A,{span:12},r.createElement(m.Ay,{type:"link"},r.createElement("span",null,"Logged in as:"," ",r.createElement("strong",null,null==y?void 0:y.email)))),r.createElement(R.A,{span:null!=y&&y.email?12:24},r.createElement(m.Ay,{type:"primary",icon:r.createElement("span",null,"🔐"),size:"large",style:{width:"100%",marginBottom:16},onClick:()=>{if("localhost"!==window.location.hostname)return v.Ay.error("Google OAuth2 login is only supported on localhost for development purposes.");let e=window.open("/google/spanner/login","googleAuth","width=600,height=700,scrollbars=yes,resizable=yes,status=yes,location=yes,toolbar=no,menubar=no,left="+(screen.width/2-300)+",top="+(screen.height/2-350));const t=setInterval(()=>{if((()=>{const e=localStorage.getItem("g_auth_token"),t=localStorage.getItem("g_auth_state"),a=localStorage.getItem("g_auth_email");return!!(e&&a&&t)})())try{var a;clearInterval(t),null===(a=e)||void 0===a||a.close(),e=null,(()=>{const e=localStorage.getItem("g_auth_token"),t=localStorage.getItem("g_auth_state"),a=localStorage.getItem("g_auth_email"),r=localStorage.getItem("g_auth_refresh_token"),n=localStorage.getItem("g_auth_expires_in");localStorage.removeItem("g_auth_token"),localStorage.removeItem("g_auth_state"),localStorage.removeItem("g_auth_email"),localStorage.removeItem("g_auth_refresh_token"),localStorage.removeItem("g_auth_expires_in"),e&&a&&t&&(g({token:e,refresh_token:r,expires_in:n,last_refreshed:Date.now()/1e3,state:t,email:a}),console.log("Google OAuth2 login successful:",a))})()}catch(e){}},500)}},null!=y&&y.email?"Re-login with Google":"Login with Google")))),"service_account"===d&&r.createElement(P.A,{title:"Service Account Configuration",size:"small",type:"inner"},r.createElement(N.A,{gutter:16},r.createElement(R.A,{span:24},r.createElement(q.A.Item,{label:"Service Account Key File",rules:[{required:!0,message:"Please upload service account key file"}]},r.createElement(U.A,{customRequest:e=>{const{file:t,onSuccess:a,onError:r}=e,n=new FileReader;n.onload=e=>{try{var n;const o=null===(n=e.target)||void 0===n?void 0:n.result,l=JSON.parse(o);"service_account"===l.type&&l.private_key&&l.client_email?(j(l),v.Ay.success("".concat(t.name," uploaded successfully")),null==a||a(l)):(v.Ay.error("Invalid service account key file format"),null==r||r(new Error("Invalid file format")))}catch(e){v.Ay.error("Failed to parse JSON file"),null==r||r(e)}},n.onerror=()=>{v.Ay.error("Failed to read file"),null==r||r(new Error("Failed to read file"))},n.readAsText(t)},onRemove:()=>{j({}),v.Ay.success("Service account key removed")},maxCount:1,accept:".json",showUploadList:{showPreviewIcon:!1,showDownloadIcon:!1}},r.createElement(m.Ay,{icon:r.createElement(G.A,null)},y&&"Re-upload Service Account Key (.json)",!y&&"Upload Service Account Key (.json)")),y&&r.createElement("div",{style:{marginTop:8,color:"#52c41a"}},"✅ Service account key uploaded successfully"))))),r.createElement(N.A,{gutter:16,style:{marginTop:16}},r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Project ID",name:"project_id",rules:[{required:!0,message:"Please select a project"}]},"service_account"===d&&r.createElement("input",{style:{width:"100%",height:32},disabled:"service_account"===d,value:null==y?void 0:y.project_id}),"service_account"!==d&&r.createElement(F.A,{style:{width:"100%",height:32},showSearch:!0,loading:h.listProjects,placeholder:h.listProjects?"Loading projects...":"Select a project",notFoundContent:h.listProjects?"Loading projects...":"No projects found",value:null==y?void 0:y.project_id,onChange:e=>{j(M(M({},y),{},{project_id:e,instance_id:null,database_id:null,graph_name:null}))},filterOption:(e,t)=>{var a;return null===(a=String(null==t?void 0:t.children))||void 0===a?void 0:a.toLowerCase().includes(e.toLowerCase())}},E.map(e=>r.createElement(J,{key:e.id,value:e.id},e.name||e.id))))),r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Instance ID",name:"instance_id",rules:[{required:!0,message:"Please select an instance"}]},r.createElement(F.A,{style:{width:"100%"},showSearch:!0,filterOption:(e,t)=>{var a;return null===(a=String(null==t?void 0:t.children))||void 0===a?void 0:a.toLowerCase().includes(e.toLowerCase())},onChange:e=>{j(M(M({},y),{},{instance_id:e,database_id:null,graph_name:null}))}},null===(t=E.find(e=>e.id===(null==y?void 0:y.project_id)))||void 0===t?void 0:t.instances.map(e=>r.createElement(J,{key:e.id,value:e.id},e.name||e.id)))))),r.createElement(N.A,{gutter:16,style:{marginTop:16}},r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Database ID",name:"database_id",rules:[{required:!0,message:"Please select a database"}]},r.createElement(F.A,{style:{width:"100%"},showSearch:!0,onChange:e=>{j(M(M({},y),{},{database_id:e,graph_name:null}))},filterOption:(e,t)=>{var a;return null===(a=String(null==t?void 0:t.children))||void 0===a?void 0:a.toLowerCase().includes(e.toLowerCase())},loading:h.listDatabases,placeholder:h.listDatabases?"Loading databases...":"Select a database",notFoundContent:h.listDatabases?"Loading databases...":"No databases found"},_.map(e=>r.createElement(J,{key:e.id,value:e.id},e.name||e.id))))),r.createElement(R.A,{span:12},r.createElement(q.A.Item,{label:"Property Graph",name:"graph_name",rules:[{required:!1}]},r.createElement(F.A,{style:{width:"100%"},showSearch:!0,onChange:e=>{j(M(M({},y),{},{graph_name:e}))},filterOption:(e,t)=>{var a;return null===(a=String(null==t?void 0:t.children))||void 0===a?void 0:a.toLowerCase().includes(e.toLowerCase())}},null===(a=_.find(e=>e.id===c.getFieldValue("database_id")))||void 0===a||null===(a=a.graphDBs)||void 0===a?void 0:a.map(e=>r.createElement(J,{key:e.id,value:e.id},e.name||e.id)),r.createElement(J,{value:""},"No Property Graph")))))),r.createElement(q.A.Item,null,r.createElement(u.A,null,r.createElement(m.Ay,{type:"primary",htmlType:"submit"},n?"Update Project":"Create Project"),r.createElement(m.Ay,{onClick:l},"Cancel"))))};var Q=a(4716),Z=a(7260),W=a(6067);const{Title:Y,Text:$}=s.A,ee=e=>{let{visible:t,project:a,onClose:n}=e;const[o,l]=(0,r.useState)(null),[c,i]=(0,r.useState)(!1),[s,d]=(0,r.useState)(null),[p,y]=(0,r.useState)(null);(0,r.useEffect)(()=>{t&&a&&g()},[t,a]);const g=async()=>{if(a){i(!0),d(null);try{const e=await L.getDatabaseInfo(a.database_type,a.name);l(e)}catch(e){d("Failed to load API information"),console.error("Error loading API info:",e)}finally{i(!1)}}},h=e=>{const t=window.location.origin;return"".concat(t).concat(e)},f=function(e,t){let a=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];const o=h(t),l=p===e;return r.createElement(P.A,{size:"small",style:{marginBottom:12}},r.createElement("div",{style:{marginBottom:8}},r.createElement($,{strong:!0},e," ",a?"(GET)":"(POST)"," ",n?" ---- (GraphXR uses the URL)":"")),r.createElement(u.A.Compact,{style:{width:"100%"}},r.createElement(z.A,{value:o,readOnly:!0,style:{width:"calc(100% - 40px)"}}),a&&r.createElement(m.Ay,{type:"default",onClick:()=>window.open(o,"_blank")},"Open"),r.createElement(m.Ay,{type:"primary",icon:l?r.createElement(W.A,null):r.createElement(O.A,null),onClick:()=>(async(e,t)=>{try{await navigator.clipboard.writeText(e),y(t),v.Ay.success("".concat(t," copied to clipboard")),setTimeout(()=>y(null),2e3)}catch(e){v.Ay.error("Failed to copy to clipboard")}})(o,e),style:{width:40}})))};return r.createElement(I.A,{title:r.createElement(u.A,null,r.createElement("span",null,"API URLs"),a&&r.createElement($,{type:"secondary"},"- ",a.name)),open:t,onCancel:n,footer:[r.createElement(m.Ay,{key:"close",onClick:n},"Close")],width:700},c&&r.createElement("div",{style:{textAlign:"center",padding:"40px 0"}},r.createElement(Q.A,{size:"large"}),r.createElement("div",{style:{marginTop:16}},"Loading API information...")),s&&r.createElement(Z.A,{message:"Error",description:s,type:"error",showIcon:!0,style:{marginBottom:16}}),o&&r.createElement("div",null,r.createElement(Z.A,{message:"Integration Guide",description:"Copy these URLs and use them in GraphXR to connect to your database.",type:"info",showIcon:!0,style:{marginBottom:16}}),o.api_urls.info&&f("Database Info API",o.api_urls.info,!0,!0),o.api_urls.query&&f("Query API",o.api_urls.query),o.api_urls.schema&&f("Schema API",o.api_urls.schema,!0),o.api_urls.graphSchema&&f("Graph Schema API",o.api_urls.graphSchema,!0),o.api_urls.sampleData&&f("Sample Data API",o.api_urls.sampleData,!0),o.version&&r.createElement(P.A,{size:"small",style:{marginTop:16}},r.createElement($,{strong:!0},"API Version: "),r.createElement($,{code:!0},o.version)),r.createElement(P.A,{size:"small",style:{marginTop:16}},r.createElement(Y,{level:5},"Usage Example"),r.createElement("pre",{style:{backgroundColor:"#f5f5f5",padding:"12px",borderRadius:"4px",fontSize:"12px"}},"// Execute a query\ncurl -X POST ".concat(h(o.api_urls.query||""),' \\\n -H "Content-Type: application/json" \\\n -d \'{\n "query": "SELECT * FROM Users LIMIT 10",\n "parameters": {}\n }\'')))))},{Title:te}=s.A,ae=()=>{const e=(0,c.Zp)();return r.createElement(P.A,null,r.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"}},r.createElement(te,{level:2,style:{margin:0}},"Database Projects"),r.createElement(u.A,null,r.createElement(m.Ay,{type:"primary",icon:r.createElement(g.A,null),onClick:()=>e("/addProject"),size:"large"},"Create New Project"))))},re=()=>{const e=(0,c.Zp)(),t=(0,c.zy)(),{projectId:a}=(0,c.g)(),[n,o]=(0,r.useState)([]),[l,i]=(0,r.useState)(!1),[s,d]=(0,r.useState)(null),p="/addProject"===t.pathname,y=t.pathname.startsWith("/editProject/"),g=p,h=y,[f,E]=(0,r.useState)(!1);(0,r.useEffect)(()=>{A()},[]),(0,r.useEffect)(()=>{if(y&&a&&n.length>0){const t=n.find(e=>e.id===a);t?d(t):(v.Ay.error("Project not found"),e("/listProjects"))}},[y,a,n,e]);const A=async()=>{i(!0);try{const e=await L.getProjects();o(e)}catch(e){v.Ay.error("Failed to load projects"),console.error("Error loading projects:",e)}finally{i(!1)}},_=()=>{e("/listProjects"),d(null)},D=e=>{switch(e){case"spanner":return"blue";case"postgresql":return"green";case"mysql":return"orange";case"mongodb":return"purple";default:return"default"}},F=[{title:"Project Name",dataIndex:"name",key:"name",render:e=>r.createElement("strong",null,e)},{title:"Database Type",dataIndex:"database_type",key:"database_type",render:e=>r.createElement(b.A,{color:D(e)},e.toUpperCase())},{title:"Created Time",dataIndex:"create_time",key:"create_time",render:e=>new Date(e).toLocaleString()},{title:"Updated Time",dataIndex:"update_time",key:"update_time",render:e=>new Date(e).toLocaleString()},{title:"Actions",key:"actions",render:(t,a)=>r.createElement(u.A,{size:"small"},r.createElement(j.A,{title:"Copy the API URL (GraphXR uses it)"},r.createElement(m.Ay,{type:"text",icon:r.createElement(O.A,null),onClick:()=>(async e=>{try{const t="".concat(window.location.origin,"/api/").concat(null==e?void 0:e.database_type,"/").concat(e.name);await navigator.clipboard.writeText(t),v.Ay.success("API URL copied to clipboard")}catch(e){v.Ay.error("Failed to copy API URL"),console.error("Error copying API URL:",e)}})(a)})),r.createElement(j.A,{title:"Show API URLs"},r.createElement(m.Ay,{type:"text",icon:r.createElement(C.A,null),onClick:()=>(d(a),void E(!0))})),r.createElement(j.A,{title:"Edit Project"},r.createElement(m.Ay,{type:"text",icon:r.createElement(k.A,null),onClick:()=>{return d(t=a),void e("/editProject/".concat(t.id));var t}})),r.createElement(w.A,{title:"Delete Project",description:"Are you sure you want to delete this project?",icon:r.createElement(x.A,{style:{color:"red"}}),onConfirm:()=>(async e=>{try{await L.deleteProject(e),v.Ay.success("Project deleted successfully"),A()}catch(e){v.Ay.error("Failed to delete project"),console.error("Error deleting project:",e)}})(a.id),okText:"Yes",cancelText:"No"},r.createElement(j.A,{title:"Delete Project"},r.createElement(m.Ay,{type:"text",danger:!0,icon:r.createElement(T.A,null)}))))}];return r.createElement("div",{className:"project-management"},!p&&!y&&r.createElement(r.Fragment,null,r.createElement(ae,null),r.createElement(P.A,{style:{marginTop:16}},r.createElement(S.A,{columns:F,dataSource:n,rowKey:"id",loading:l,pagination:{pageSize:10,showSizeChanger:!0,showQuickJumper:!0,showTotal:(e,t)=>"".concat(t[0],"-").concat(t[1]," of ").concat(e," items")}}))),r.createElement(I.A,{title:"Create New Project",open:g,onCancel:_,footer:null,width:800},r.createElement(H,{onSubmit:async t=>{try{await L.createProject(t),v.Ay.success("Project created successfully"),e("/listProjects"),A()}catch(e){v.Ay.error("Failed to create project"),console.error("Error creating project:",e)}},onCancel:_})),r.createElement(I.A,{title:"Edit Project",open:h,onCancel:_,footer:null,width:800},r.createElement(H,{initialValues:s,onSubmit:async t=>{if(s)try{await L.updateProject(s.id,t),v.Ay.success("Project updated successfully"),e("/listProjects"),A()}catch(e){v.Ay.error("Failed to update project"),console.error("Error updating project:",e)}},onCancel:_})),r.createElement(ee,{visible:f,project:s,onClose:()=>E(!1)}))},{Content:ne}=i.A,oe=()=>r.createElement(i.A,{className:"app-layout"},r.createElement(_,null),r.createElement(ne,{className:"app-content"},r.createElement("div",{className:"content-wrapper"},r.createElement(c.BV,null,r.createElement(c.qh,{path:"/",element:r.createElement(re,null)}),r.createElement(c.qh,{path:"/listProjects",element:r.createElement(re,null)}),r.createElement(c.qh,{path:"/addProject",element:r.createElement(re,null)}),r.createElement(c.qh,{path:"/editProject/:projectId",element:r.createElement(re,null)}),r.createElement(c.qh,{path:"/admin",element:r.createElement(re,null)}),r.createElement(c.qh,{path:"/projects",element:r.createElement(re,null)})))));n.createRoot(document.getElementById("root")).render(r.createElement(r.StrictMode,null,r.createElement(l.Ay,{theme:{token:{colorPrimary:"#1890ff",borderRadius:6}}},r.createElement(o.Kd,null,r.createElement(oe,null)))))}},n={};function o(e){var t=n[e];if(void 0!==t)return t.exports;var a=n[e]={exports:{}};return r[e](a,a.exports,o),a.exports}o.m=r,e=[],o.O=(t,a,r,n)=>{if(!a){var l=1/0;for(u=0;u<e.length;u++){for(var[a,r,n]=e[u],c=!0,i=0;i<a.length;i++)(!1&n||l>=n)&&Object.keys(o.O).every(e=>o.O[e](a[i]))?a.splice(i--,1):(c=!1,n<l&&(l=n));if(c){e.splice(u--,1);var s=r();void 0!==s&&(t=s)}}return t}n=n||0;for(var u=e.length;u>0&&e[u-1][2]>n;u--)e[u]=e[u-1];e[u]=[a,r,n]},o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,o.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var n=Object.create(null);o.r(n);var l={};t=t||[null,a({}),a([]),a(a)];for(var c=2&r&&e;("object"==typeof c||"function"==typeof c)&&!~t.indexOf(c);c=a(c))Object.getOwnPropertyNames(c).forEach(t=>l[t]=()=>e[t]);return l.default=()=>e,o.d(n,l),n},o.d=(e,t)=>{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e={792:0};o.O.j=t=>0===e[t];var t=(t,a)=>{var r,n,[l,c,i]=a,s=0;if(l.some(t=>0!==e[t])){for(r in c)o.o(c,r)&&(o.m[r]=c[r]);if(i)var u=i(o)}for(t&&t(a);s<l.length;s++)n=l[s],o.o(e,n)&&e[n]&&e[n][0](),e[n]=0;return o.O(u)},a=self.webpackChunkgraphxr_database_proxy_frontend=self.webpackChunkgraphxr_database_proxy_frontend||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})();var l=o.O(void 0,[96],()=>o(2465));l=o.O(l)})();
|
|
2
|
+
//# sourceMappingURL=main.ce3fbb85a7bc9452edb9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.ce3fbb85a7bc9452edb9.js","mappings":"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","sources":["webpack://graphxr-database-proxy-frontend/webpack/runtime/chunk loaded","webpack://graphxr-database-proxy-frontend/webpack/runtime/create fake namespace object","webpack://graphxr-database-proxy-frontend/./src/components/AppHeader.tsx","webpack://graphxr-database-proxy-frontend/./src/services/projectService.ts","webpack://graphxr-database-proxy-frontend/./src/components/ProjectForm.tsx","webpack://graphxr-database-proxy-frontend/./src/components/ApiUrlsModal.tsx","webpack://graphxr-database-proxy-frontend/./src/components/ProjectListHeader.tsx","webpack://graphxr-database-proxy-frontend/./src/pages/ProjectManagement.tsx","webpack://graphxr-database-proxy-frontend/./src/App.tsx","webpack://graphxr-database-proxy-frontend/./src/index.tsx","webpack://graphxr-database-proxy-frontend/webpack/bootstrap","webpack://graphxr-database-proxy-frontend/webpack/runtime/compat get default export","webpack://graphxr-database-proxy-frontend/webpack/runtime/define property getters","webpack://graphxr-database-proxy-frontend/webpack/runtime/global","webpack://graphxr-database-proxy-frontend/webpack/runtime/hasOwnProperty shorthand","webpack://graphxr-database-proxy-frontend/webpack/runtime/make namespace object","webpack://graphxr-database-proxy-frontend/webpack/runtime/jsonp chunk loading","webpack://graphxr-database-proxy-frontend/webpack/startup"],"sourcesContent":["var deferred = [];\n__webpack_require__.O = (result, chunkIds, fn, priority) => {\n\tif(chunkIds) {\n\t\tpriority = priority || 0;\n\t\tfor(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1];\n\t\tdeferred[i] = [chunkIds, fn, priority];\n\t\treturn;\n\t}\n\tvar notFulfilled = Infinity;\n\tfor (var i = 0; i < deferred.length; i++) {\n\t\tvar [chunkIds, fn, priority] = deferred[i];\n\t\tvar fulfilled = true;\n\t\tfor (var j = 0; j < chunkIds.length; j++) {\n\t\t\tif ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) {\n\t\t\t\tchunkIds.splice(j--, 1);\n\t\t\t} else {\n\t\t\t\tfulfilled = false;\n\t\t\t\tif(priority < notFulfilled) notFulfilled = priority;\n\t\t\t}\n\t\t}\n\t\tif(fulfilled) {\n\t\t\tdeferred.splice(i--, 1)\n\t\t\tvar r = fn();\n\t\t\tif (r !== undefined) result = r;\n\t\t}\n\t}\n\treturn result;\n};","var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; (typeof current == 'object' || typeof current == 'function') && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","import React from 'react';\r\nimport { Layout, Typography, Space, Button, Menu } from 'antd';\r\nimport { DatabaseOutlined, ApiOutlined, HomeOutlined, PlusOutlined, UnorderedListOutlined } from '@ant-design/icons';\r\nimport { useNavigate, useLocation } from 'react-router-dom';\r\n\r\nconst { Header } = Layout;\r\nconst { Title } = Typography;\r\n\r\nconst AppHeader: React.FC = () => {\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n\r\n const handleDocsClick = () => {\r\n window.open('/docs', '_blank');\r\n };\r\n\r\n const menuItems = [\r\n {\r\n key: '/',\r\n icon: <HomeOutlined />,\r\n label: 'Home',\r\n },\r\n {\r\n key: '/listProjects',\r\n icon: <UnorderedListOutlined />,\r\n label: 'Projects',\r\n },\r\n {\r\n key: '/addProject',\r\n icon: <PlusOutlined />,\r\n label: 'Add Project',\r\n },\r\n ];\r\n\r\n const handleMenuClick = (e: { key: string }) => {\r\n navigate(e.key);\r\n };\r\n\r\n return (\r\n <Header className=\"app-header\">\r\n <div className=\"header-content\">\r\n <Space align=\"center\">\r\n <DatabaseOutlined className=\"logo-icon\" />\r\n <Title level={3} className=\"title\" style={{color: 'white', margin: 0}}>\r\n GraphXR Database Proxy\r\n </Title>\r\n </Space>\r\n \r\n <Menu\r\n theme=\"dark\"\r\n mode=\"horizontal\"\r\n selectedKeys={[location.pathname]}\r\n items={menuItems}\r\n onClick={handleMenuClick}\r\n style={{ flex: 1, justifyContent: 'end' }}\r\n />\r\n \r\n <Space>\r\n <Button \r\n type=\"text\" \r\n icon={<ApiOutlined />} \r\n onClick={handleDocsClick}\r\n className=\"header-button\"\r\n >\r\n API Docs\r\n </Button>\r\n </Space>\r\n </div>\r\n </Header>\r\n );\r\n};\r\n\r\nexport default AppHeader;","import axios from 'axios';\r\nimport { Project, ProjectCreate, ProjectUpdate, APIInfo, QueryRequest, QueryResponse, SchemaResponse, GraphSchemaResponse, SampleDataResponse } from '../types/project';\r\n\r\nconst API_BASE_URL = process.env.NODE_ENV === 'production' ? '' : 'http://localhost:9080';\r\n\r\nconst api = axios.create({\r\n baseURL: API_BASE_URL,\r\n timeout: 30000,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n});\r\n\r\n// Request interceptor\r\napi.interceptors.request.use(\r\n (config) => {\r\n return config;\r\n },\r\n (error) => {\r\n return Promise.reject(error);\r\n }\r\n);\r\n\r\n// Response interceptor\r\napi.interceptors.response.use(\r\n (response) => {\r\n return response;\r\n },\r\n (error) => {\r\n console.error('API Error:', error);\r\n return Promise.reject(error);\r\n }\r\n);\r\n\r\nexport class ProjectService {\r\n async getProjects(): Promise<Project[]> {\r\n const response = await api.get('/api/project/list');\r\n return response.data;\r\n }\r\n\r\n async getProject(id: string): Promise<Project> {\r\n const response = await api.get(`/api/project/${id}`);\r\n return response.data;\r\n }\r\n\r\n async createProject(project: ProjectCreate): Promise<Project> {\r\n const response = await api.post('/api/project/create', project);\r\n return response.data;\r\n }\r\n\r\n async updateProject(id: string, project: ProjectUpdate): Promise<Project> {\r\n const response = await api.put(`/api/project/update?project_id=${id}`, project);\r\n return response.data;\r\n }\r\n\r\n async deleteProject(id: string): Promise<void> {\r\n await api.delete(`/api/project/delete?project_id=${id}`);\r\n }\r\n\r\n async getDatabaseInfo(databaseType: string, projectName: string): Promise<APIInfo> {\r\n const response = await api.get(`/api/${databaseType}/${projectName}`);\r\n return response.data;\r\n }\r\n\r\n async executeQuery(\r\n databaseType: string,\r\n projectName: string,\r\n queryRequest: QueryRequest\r\n ): Promise<QueryResponse> {\r\n const response = await api.post(`/api/${databaseType}/${projectName}/query`, queryRequest);\r\n return response.data;\r\n }\r\n\r\n async getSchema(databaseType: string, projectName: string): Promise<SchemaResponse> {\r\n const response = await api.get(`/api/${databaseType}/${projectName}/schema`);\r\n return response.data;\r\n }\r\n\r\n async getGraphSchema(databaseType: string, projectName: string): Promise<GraphSchemaResponse> {\r\n const response = await api.get(`/api/${databaseType}/${projectName}/graphSchema`);\r\n return response.data;\r\n }\r\n\r\n async getSampleData(databaseType: string, projectName: string): Promise<SampleDataResponse> {\r\n const response = await api.get(`/api/${databaseType}/${projectName}/sampleData`);\r\n return response.data;\r\n }\r\n\r\n async testConnection(databaseType: string, projectName: string): Promise<{ success: boolean; message: string }> {\r\n const response = await api.post(`/api/${databaseType}/${projectName}/test`);\r\n return response.data;\r\n }\r\n}\r\n\r\nexport const projectService = new ProjectService();","import React, { useEffect } from \"react\";\r\nimport {\r\n Form,\r\n Input,\r\n Select,\r\n Button,\r\n Card,\r\n Row,\r\n Col,\r\n Space,\r\n InputNumber,\r\n Upload,\r\n message,\r\n Alert,\r\n} from \"antd\";\r\nimport { UploadOutlined, ClockCircleOutlined } from \"@ant-design/icons\";\r\nimport type { UploadProps } from \"antd\";\r\nimport {\r\n DatabaseType,\r\n AuthType,\r\n Project,\r\n ProjectCreate,\r\n} from \"../types/project\";\r\n\r\nconst { Option } = Select;\r\n\r\ninterface ProjectFormProps {\r\n initialValues?: Project | null;\r\n onSubmit: (values: ProjectCreate) => void;\r\n onCancel: () => void;\r\n}\r\n\r\n\r\n//window is localhost:9080 use the oauth2 auth by default, otherwise use service account by default\r\n//@todo maybe we can support other auth types in the future\r\nconst DefaultAuthType = window.location.origin === \"http://localhost:9080\" ? \"oauth2\" : \"service_account\";\r\n\r\n\r\nconst ProjectForm: React.FC<ProjectFormProps> = ({\r\n initialValues,\r\n onSubmit,\r\n onCancel,\r\n}) => {\r\n const [form] = Form.useForm();\r\n const [databaseType, setDatabaseType] =\r\n React.useState<DatabaseType>(\"spanner\");\r\n const [authType, setAuthType] = React.useState<AuthType>(DefaultAuthType);\r\n const [serviceAccountKey, setServiceAccountKey] = React.useState<any>({});\r\n // const [showOauthAdvanced, setShowOauthAdvanced] = React.useState(false);\r\n const [loading, setLoading] = React.useState({\r\n listProjects: false,\r\n listDatabases: false,\r\n });\r\n const [projectsList, setProjectsList] = React.useState<\r\n Array<{ id: string; name: string; instances: Array<{ id: string; name: string }> }>\r\n >([]);\r\n const [databases, setDatabases] = React.useState<\r\n Array<{ id: string; name: string; graphDBs?: Array<{ id: string; name: string }> }>\r\n >([]);\r\n\r\n useEffect(() => {\r\n if (initialValues) {\r\n form.setFieldsValue({\r\n name: initialValues.name,\r\n database_type: initialValues.database_type,\r\n ...initialValues.database_config,\r\n });\r\n setDatabaseType(initialValues.database_type);\r\n setAuthType(initialValues.database_config.auth_type);\r\n updateServiceAccountKey({\r\n ...initialValues.database_config.oauth_config,\r\n });\r\n }\r\n }, [initialValues, form]);\r\n\r\n const handleSubmit = (values: any) => {\r\n const { name, database_type, ...dbConfigFields } = values;\r\n\r\n let databaseConfig: any = {\r\n type: database_type,\r\n auth_type: authType,\r\n options: {},\r\n };\r\n\r\n if (database_type === \"spanner\") {\r\n databaseConfig = {\r\n ...databaseConfig,\r\n project_id: dbConfigFields.project_id,\r\n instance_id: dbConfigFields.instance_id,\r\n database_id: dbConfigFields.database_id,\r\n graph_name: dbConfigFields.graph_name || \"\",\r\n };\r\n\r\n if (authType === \"oauth2\") {\r\n databaseConfig.oauth_config = {\r\n ...serviceAccountKey,\r\n };\r\n } else if (authType === \"service_account\") {\r\n if (!serviceAccountKey) {\r\n message.error(\"Please upload a service account key file\");\r\n return;\r\n }\r\n databaseConfig.project_id = serviceAccountKey?.project_id;\r\n databaseConfig.oauth_config = {\r\n ...serviceAccountKey,\r\n };\r\n }\r\n\r\n // databaseConfig.options = {\r\n // max_sessions: dbConfigFields.max_sessions || 100,\r\n // timeout: dbConfigFields.timeout || 30,\r\n // read_only: dbConfigFields.read_only || false,\r\n // };\r\n }\r\n\r\n const projectData: ProjectCreate = {\r\n name,\r\n database_type,\r\n database_config: databaseConfig,\r\n };\r\n\r\n onSubmit(projectData);\r\n };\r\n\r\n const handleDatabaseTypeChange = (value: DatabaseType) => {\r\n setDatabaseType(value);\r\n form.resetFields([\r\n \"project_id\",\r\n \"instance_id\",\r\n \"database_id\",\r\n \"host\",\r\n \"port\",\r\n ]);\r\n };\r\n\r\n const handleAuthTypeChange = (value: AuthType) => {\r\n setAuthType(value);\r\n form.resetFields([\"client_id\", \"client_secret\", \"username\", \"password\"]);\r\n updateServiceAccountKey({});\r\n };\r\n\r\n const handleServiceAccountUpload: UploadProps[\"customRequest\"] = (\r\n options\r\n ) => {\r\n const { file, onSuccess, onError } = options;\r\n\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n try {\r\n const content = e.target?.result as string;\r\n const jsonContent = JSON.parse(content);\r\n\r\n // 验证是否为有效的服务账号文件\r\n if (\r\n jsonContent.type === \"service_account\" &&\r\n jsonContent.private_key &&\r\n jsonContent.client_email\r\n ) {\r\n updateServiceAccountKey(jsonContent);\r\n message.success(`${(file as File).name} uploaded successfully`);\r\n onSuccess?.(jsonContent);\r\n } else {\r\n message.error(\"Invalid service account key file format\");\r\n onError?.(new Error(\"Invalid file format\"));\r\n }\r\n } catch (error) {\r\n message.error(\"Failed to parse JSON file\");\r\n onError?.(error as Error);\r\n }\r\n };\r\n\r\n reader.onerror = () => {\r\n message.error(\"Failed to read file\");\r\n onError?.(new Error(\"Failed to read file\"));\r\n };\r\n\r\n reader.readAsText(file as File);\r\n };\r\n\r\n const handleRemoveServiceAccount = () => {\r\n updateServiceAccountKey({});\r\n message.success(\"Service account key removed\");\r\n };\r\n\r\n const getProjectsList = async () => {\r\n try {\r\n setLoading({ listDatabases: false, listProjects: true });\r\n const response = await fetch(\"/api/google/spanner/list_projects\", {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({\r\n auth: serviceAccountKey,\r\n }),\r\n });\r\n if (response.ok) {\r\n const projects = await response.json();\r\n setProjectsList(projects);\r\n } else {\r\n message.error(\"Failed to fetch projects\");\r\n }\r\n setLoading({ listDatabases: false, listProjects: false });\r\n } catch (error) {\r\n message.error(\"Error fetching projects\");\r\n }\r\n };\r\n\r\n const getDatabaseList = async () => {\r\n try {\r\n setLoading({ listDatabases: true, listProjects: false });\r\n const response = await fetch(\"/api/google/spanner/list_databases\", {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n body: JSON.stringify({\r\n auth: serviceAccountKey,\r\n }),\r\n });\r\n if (response.ok) {\r\n const databases = await response.json();\r\n setDatabases(databases);\r\n } else {\r\n message.error(\"Failed to fetch databases\");\r\n }\r\n setLoading({ listDatabases: false, listProjects: false });\r\n } catch (error) {\r\n message.error(\"Error fetching databases\");\r\n }\r\n };\r\n\r\n\r\n\r\n useEffect(() => {\r\n\r\n const isNewOauth2 = serviceAccountKey && serviceAccountKey?.token && authType == \"oauth2\";\r\n const isNewServiceAccount = serviceAccountKey && serviceAccountKey?.type === \"service_account\" && serviceAccountKey.private_key_id;\r\n if (\r\n isNewOauth2 ||\r\n isNewServiceAccount\r\n ) {\r\n getProjectsList();\r\n }\r\n }, [serviceAccountKey?.token, authType, serviceAccountKey?.type , serviceAccountKey.private_key_id]);\r\n\r\n useEffect(() => {\r\n if (serviceAccountKey && serviceAccountKey?.project_id && serviceAccountKey?.instance_id) {\r\n getDatabaseList();\r\n }\r\n }, [serviceAccountKey?.project_id, serviceAccountKey?.instance_id]);\r\n\r\n\r\n const handleGoogleLogin = () => {\r\n if (window.location.hostname !== \"localhost\") {\r\n return message.error(\r\n \"Google OAuth2 login is only supported on localhost for development purposes.\"\r\n );\r\n }\r\n const authUrl = `/google/spanner/login`;\r\n\r\n // Open popup window instead of iframe\r\n let popup = window.open(\r\n authUrl,\r\n \"googleAuth\",\r\n \"width=600,height=700,scrollbars=yes,resizable=yes,status=yes,location=yes,toolbar=no,menubar=no,left=\" +\r\n (screen.width / 2 - 300) +\r\n \",top=\" +\r\n (screen.height / 2 - 350)\r\n );\r\n\r\n //read the g_auth_token , g_auth_state, g_auth_email from localStorage, then clean up the localStorage\r\n\r\n const handleGoogleLoginData = () => {\r\n const token = localStorage.getItem(\"g_auth_token\");\r\n const state = localStorage.getItem(\"g_auth_state\");\r\n const email = localStorage.getItem(\"g_auth_email\");\r\n const refresh_token = localStorage.getItem(\"g_auth_refresh_token\"); \r\n const expires_in = localStorage.getItem(\"g_auth_expires_in\");\r\n\r\n localStorage.removeItem(\"g_auth_token\");\r\n localStorage.removeItem(\"g_auth_state\");\r\n localStorage.removeItem(\"g_auth_email\");\r\n localStorage.removeItem(\"g_auth_refresh_token\");\r\n localStorage.removeItem(\"g_auth_expires_in\");\r\n\r\n if (token && email && state) {\r\n setServiceAccountKey({\r\n token,\r\n refresh_token,\r\n expires_in,\r\n last_refreshed: Date.now()/1000,\r\n state,\r\n email,\r\n });\r\n console.log(\"Google OAuth2 login successful:\", email);\r\n }\r\n };\r\n\r\n const checkGoogleLoginDataWrited = () => {\r\n // Check if the data has been written to localStorage\r\n const token = localStorage.getItem(\"g_auth_token\");\r\n const state = localStorage.getItem(\"g_auth_state\");\r\n const email = localStorage.getItem(\"g_auth_email\");\r\n\r\n if (token && email && state) {\r\n return true;\r\n }\r\n return false;\r\n };\r\n\r\n // Listen for popup to close\r\n const timer = setInterval(() => {\r\n if (checkGoogleLoginDataWrited()) {\r\n try {\r\n clearInterval(timer);\r\n popup?.close();\r\n popup = null;\r\n handleGoogleLoginData();\r\n } catch (e) {\r\n // Ignore if popup is already closed\r\n }\r\n }\r\n }, 500);\r\n };\r\n\r\n const updateServiceAccountKey = (newData: any) => {\r\n const newService = {\r\n ...serviceAccountKey,\r\n ...newData,\r\n };\r\n setServiceAccountKey((prevKey: any) => ({\r\n ...prevKey,\r\n ...newData,\r\n }));\r\n form.setFieldsValue({\r\n project_id: newService.project_id,\r\n instance_id: newService.instance_id,\r\n database_id: newService.database_id,\r\n graph_name: newService.graph_name,\r\n });\r\n };\r\n\r\n return (\r\n <Form\r\n form={form}\r\n layout=\"vertical\"\r\n onFinish={handleSubmit}\r\n initialValues={{\r\n redirect_uri: \"http://localhost:9080/google/spanner/callback\",\r\n max_sessions: 100,\r\n timeout: 30,\r\n read_only: true,\r\n auth_type: authType,\r\n database_type: \"spanner\",\r\n }}\r\n >\r\n <Row gutter={16}>\r\n <Col span={24}>\r\n <Form.Item\r\n label=\"Project Name\"\r\n name=\"name\"\r\n rules={[{ required: true, message: \"Please enter project name\" }]}\r\n >\r\n <Input placeholder=\"Enter project name\" />\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n\r\n <Row gutter={16}>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Database Type\"\r\n name=\"database_type\"\r\n rules={[{ required: true, message: \"Please select database type\" }]}\r\n >\r\n <Select\r\n placeholder=\"Select database type\"\r\n onChange={handleDatabaseTypeChange}\r\n >\r\n <Option value=\"spanner\">Google Cloud Spanner</Option>\r\n {/* <Option value=\"postgresql\">PostgreSQL</Option>\r\n <Option value=\"mysql\">MySQL</Option>\r\n <Option value=\"mongodb\">MongoDB</Option> */}\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Authentication Type\"\r\n name=\"auth_type\"\r\n rules={[\r\n { required: true, message: \"Please select authentication type\" },\r\n ]}\r\n >\r\n <Select\r\n placeholder=\"Select authentication type\"\r\n onChange={handleAuthTypeChange}\r\n >\r\n <Option value=\"oauth2\">OAuth2</Option>\r\n <Option value=\"service_account\">Service Account</Option>\r\n {/* <Option value=\"username_password\">Username/Password</Option> */}\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n\r\n {databaseType === \"spanner\" && (\r\n <Card\r\n title=\"Spanner Configuration\"\r\n size=\"small\"\r\n style={{ marginBottom: 16 }}\r\n >\r\n {authType === \"oauth2\" && (\r\n <Card title=\"OAuth2 Configuration\" size=\"small\" type=\"inner\">\r\n <Row gutter={16}>\r\n {serviceAccountKey?.email && (\r\n <Col span={12}>\r\n <Button type=\"link\">\r\n <span>\r\n Logged in as:{\" \"}\r\n <strong>{serviceAccountKey?.email}</strong>\r\n </span>\r\n </Button>\r\n </Col>\r\n )}\r\n <Col span={serviceAccountKey?.email ? 12 : 24}>\r\n <Button\r\n type=\"primary\"\r\n icon={<span>🔐</span>}\r\n size=\"large\"\r\n style={{ width: \"100%\", marginBottom: 16 }}\r\n onClick={handleGoogleLogin}\r\n >\r\n {serviceAccountKey?.email\r\n ? \"Re-login with Google\"\r\n : \"Login with Google\"}\r\n </Button>\r\n </Col>\r\n </Row>\r\n {/* <Row justify=\"end\">\r\n <Button\r\n type=\"link\"\r\n onClick={() => setShowOauthAdvanced(!showOauthAdvanced)}\r\n style={{ paddingRight: 0 }}\r\n >\r\n {showOauthAdvanced\r\n ? \"Hide Advanced Settings\"\r\n : \"Advanced Settings\"}\r\n </Button>\r\n </Row> */}\r\n {/* {showOauthAdvanced && (\r\n <>\r\n <Row gutter={16}>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Client ID\"\r\n name=\"client_id\"\r\n rules={[\r\n { required: true, message: \"Please enter client ID\" },\r\n ]}\r\n >\r\n <Input placeholder=\"your-oauth-client-id\" />\r\n </Form.Item>\r\n </Col>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Client Secret\"\r\n name=\"client_secret\"\r\n rules={[\r\n {\r\n required: true,\r\n message: \"Please enter client secret\",\r\n },\r\n ]}\r\n >\r\n <Input.Password placeholder=\"your-oauth-client-secret\" />\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n <Row gutter={16}>\r\n <Col span={24}>\r\n <Form.Item label=\"Redirect URI\" name=\"redirect_uri\">\r\n <Input placeholder=\"http://localhost:9080/google/auth/callback\" />\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n </>\r\n )} */}\r\n </Card>\r\n )}\r\n\r\n {authType === \"service_account\" && (\r\n <Card\r\n title=\"Service Account Configuration\"\r\n size=\"small\"\r\n type=\"inner\"\r\n >\r\n <Row gutter={16}>\r\n <Col span={24}>\r\n <Form.Item\r\n label=\"Service Account Key File\"\r\n rules={[\r\n {\r\n required: true,\r\n message: \"Please upload service account key file\",\r\n },\r\n ]}\r\n >\r\n <Upload\r\n customRequest={handleServiceAccountUpload}\r\n onRemove={handleRemoveServiceAccount}\r\n maxCount={1}\r\n accept=\".json\"\r\n showUploadList={{\r\n showPreviewIcon: false,\r\n showDownloadIcon: false,\r\n }}\r\n >\r\n <Button icon={<UploadOutlined />}>\r\n {serviceAccountKey && \"Re-upload Service Account Key (.json)\"}\r\n {!serviceAccountKey && \"Upload Service Account Key (.json)\"}\r\n </Button>\r\n </Upload>\r\n {serviceAccountKey && (\r\n <div style={{ marginTop: 8, color: \"#52c41a\" }}>\r\n ✅ Service account key uploaded successfully\r\n </div>\r\n )}\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n </Card>\r\n )}\r\n\r\n <Row gutter={16} style={{ marginTop: 16 }}>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Project ID\"\r\n name=\"project_id\"\r\n rules={[{ required: true, message: \"Please select a project\" }]}\r\n >\r\n {authType === \"service_account\" && (\r\n <input\r\n style={{ width: \"100%\", height: 32 }}\r\n disabled={authType === \"service_account\"}\r\n value={serviceAccountKey?.project_id}\r\n />\r\n )}\r\n {authType !== \"service_account\" && (\r\n <Select style={{ width: \"100%\", height: 32 }} showSearch\r\n loading={loading.listProjects}\r\n placeholder={loading.listProjects ? \"Loading projects...\" : \"Select a project\"}\r\n notFoundContent={loading.listProjects ? \"Loading projects...\" : \"No projects found\"}\r\n value={serviceAccountKey?.project_id}\r\n onChange={(value) => {\r\n updateServiceAccountKey({\r\n ...serviceAccountKey,\r\n project_id: value,\r\n instance_id: null,\r\n database_id: null,\r\n graph_name: null,\r\n })\r\n }}\r\n filterOption={(input, option) =>\r\n String(option?.children)?.toLowerCase().includes(input.toLowerCase())\r\n }>\r\n {projectsList.map((proj) => (\r\n <Option key={proj.id} value={proj.id}>\r\n {proj.name || proj.id} \r\n </Option>\r\n ))}\r\n </Select>\r\n )}\r\n </Form.Item>\r\n </Col>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Instance ID\"\r\n name=\"instance_id\"\r\n rules={[\r\n { required: true, message: \"Please select an instance\" },\r\n ]}\r\n >\r\n <Select style={{ width: \"100%\" }} showSearch\r\n filterOption={(input, option) =>\r\n String(option?.children)?.toLowerCase().includes(input.toLowerCase())\r\n }\r\n onChange={(value) => {\r\n updateServiceAccountKey({\r\n ...serviceAccountKey,\r\n instance_id: value,\r\n database_id: null,\r\n graph_name: null,\r\n });\r\n }}\r\n >\r\n {projectsList.find((p) => p.id === serviceAccountKey?.project_id)?.instances.map((inst) => (\r\n <Option key={inst.id} value={inst.id}>\r\n {inst.name || inst.id}\r\n </Option>\r\n ))}\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n <Row gutter={16} style={{ marginTop: 16 }}>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Database ID\"\r\n name=\"database_id\"\r\n rules={[\r\n { required: true, message: \"Please select a database\" },\r\n ]}\r\n >\r\n <Select style={{ width: \"100%\" }} showSearch\r\n onChange={(value) => {\r\n updateServiceAccountKey({\r\n ...serviceAccountKey,\r\n database_id: value,\r\n graph_name: null,\r\n });\r\n }}\r\n filterOption={(input, option) =>\r\n String(option?.children)?.toLowerCase().includes(input.toLowerCase())\r\n }\r\n loading={loading.listDatabases}\r\n placeholder={loading.listDatabases ? \"Loading databases...\" : \"Select a database\"}\r\n notFoundContent={loading.listDatabases ? \"Loading databases...\" : \"No databases found\"}\r\n >\r\n { databases.map((db) => (\r\n <Option key={db.id} value={db.id}>\r\n {db.name || db.id}\r\n </Option>\r\n ))}\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n <Col span={12}>\r\n <Form.Item\r\n label=\"Property Graph\"\r\n name=\"graph_name\"\r\n rules={[{ required: false }]}\r\n >\r\n <Select style={{ width: \"100%\" }} showSearch\r\n onChange={(value) => {\r\n updateServiceAccountKey({\r\n ...serviceAccountKey,\r\n graph_name: value,\r\n });\r\n }}\r\n filterOption={(input, option) =>\r\n String(option?.children)?.toLowerCase().includes(input.toLowerCase())\r\n }\r\n >\r\n { databases.find((db) => db.id === form.getFieldValue(\"database_id\"))?.graphDBs?.map((graphDB) => (\r\n <Option key={graphDB.id} value={graphDB.id}>\r\n {graphDB.name || graphDB.id}\r\n </Option>\r\n ))}\r\n <Option value={\"\"}>No Property Graph</Option>\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n\r\n {/* <Card title=\"Connection Options\" size=\"small\" type=\"inner\">\r\n <Row gutter={16}>\r\n <Col span={8}>\r\n <Form.Item label=\"Max Sessions\" name=\"max_sessions\">\r\n <InputNumber min={1} max={1000} style={{ width: \"100%\" }} />\r\n </Form.Item>\r\n </Col>\r\n <Col span={8}>\r\n <Form.Item label=\"Timeout (seconds)\" name=\"timeout\">\r\n <InputNumber min={1} max={300} style={{ width: \"100%\" }} />\r\n </Form.Item>\r\n </Col>\r\n <Col span={8}>\r\n <Form.Item\r\n label=\"Read Only\"\r\n name=\"read_only\"\r\n valuePropName=\"checked\"\r\n >\r\n <Select style={{ width: \"100%\" }}>\r\n <Option value={false}>No</Option>\r\n <Option value={true}>Yes</Option>\r\n </Select>\r\n </Form.Item>\r\n </Col>\r\n </Row>\r\n </Card> */}\r\n </Card>\r\n )}\r\n\r\n <Form.Item>\r\n <Space>\r\n <Button type=\"primary\" htmlType=\"submit\">\r\n {initialValues ? \"Update Project\" : \"Create Project\"}\r\n </Button>\r\n <Button onClick={onCancel}>Cancel</Button>\r\n </Space>\r\n </Form.Item>\r\n </Form>\r\n );\r\n};\r\n\r\nexport default ProjectForm;\r\n","import React, { useState, useEffect } from 'react';\r\nimport {\r\n Modal,\r\n Card,\r\n Input,\r\n Button,\r\n Space,\r\n Typography,\r\n message,\r\n Spin,\r\n Alert,\r\n} from 'antd';\r\nimport { CopyOutlined, CheckOutlined } from '@ant-design/icons';\r\nimport { Project, APIInfo } from '../types/project';\r\nimport { projectService } from '../services/projectService';\r\n\r\nconst { Title, Text } = Typography;\r\n\r\ninterface ApiUrlsModalProps {\r\n visible: boolean;\r\n project: Project | null;\r\n onClose: () => void;\r\n}\r\n\r\nconst ApiUrlsModal: React.FC<ApiUrlsModalProps> = ({\r\n visible,\r\n project,\r\n onClose,\r\n}) => {\r\n const [apiInfo, setApiInfo] = useState<APIInfo | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [copiedUrl, setCopiedUrl] = useState<string | null>(null);\r\n\r\n useEffect(() => {\r\n if (visible && project) {\r\n loadApiInfo();\r\n }\r\n }, [visible, project]);\r\n\r\n const loadApiInfo = async () => {\r\n if (!project) return;\r\n\r\n setLoading(true);\r\n setError(null);\r\n try {\r\n const info = await projectService.getDatabaseInfo(\r\n project.database_type,\r\n project.name\r\n );\r\n setApiInfo(info);\r\n } catch (err) {\r\n setError('Failed to load API information');\r\n console.error('Error loading API info:', err);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n const copyToClipboard = async (url: string, label: string) => {\r\n try {\r\n await navigator.clipboard.writeText(url);\r\n setCopiedUrl(label);\r\n message.success(`${label} copied to clipboard`);\r\n setTimeout(() => setCopiedUrl(null), 2000);\r\n } catch (err) {\r\n message.error('Failed to copy to clipboard');\r\n }\r\n };\r\n\r\n const getFullUrl = (relativePath: string) => {\r\n const baseUrl = window.location.origin;\r\n return `${baseUrl}${relativePath}`;\r\n };\r\n\r\n const renderUrlInput = (label: string, relativePath: string, isGet = false, isForGraphXR = false) => {\r\n const fullUrl = getFullUrl(relativePath);\r\n const isCopied = copiedUrl === label;\r\n\r\n return (\r\n <Card size=\"small\" style={{ marginBottom: 12 }}>\r\n <div style={{ marginBottom: 8 }}>\r\n <Text strong>{label} {isGet ? '(GET)' : '(POST)'} {isForGraphXR ? ' ---- (GraphXR uses the URL)' : ''}</Text>\r\n </div>\r\n <Space.Compact style={{ width: '100%' }}>\r\n <Input\r\n value={fullUrl}\r\n readOnly\r\n style={{ width: 'calc(100% - 40px)' }}\r\n />\r\n {isGet && (\r\n <Button\r\n type=\"default\" \r\n onClick={() => window.open(fullUrl, '_blank')}\r\n >Open</Button>\r\n )}\r\n <Button\r\n type=\"primary\"\r\n icon={isCopied ? <CheckOutlined /> : <CopyOutlined />}\r\n onClick={() => copyToClipboard(fullUrl, label)}\r\n style={{ width: 40 }}\r\n />\r\n </Space.Compact>\r\n </Card>\r\n );\r\n };\r\n\r\n return (\r\n <Modal\r\n title={\r\n <Space>\r\n <span>API URLs</span>\r\n {project && (\r\n <Text type=\"secondary\">- {project.name}</Text>\r\n )}\r\n </Space>\r\n }\r\n open={visible}\r\n onCancel={onClose}\r\n footer={[\r\n <Button key=\"close\" onClick={onClose}>\r\n Close\r\n </Button>,\r\n ]}\r\n width={700}\r\n >\r\n {loading && (\r\n <div style={{ textAlign: 'center', padding: '40px 0' }}>\r\n <Spin size=\"large\" />\r\n <div style={{ marginTop: 16 }}>Loading API information...</div>\r\n </div>\r\n )}\r\n\r\n {error && (\r\n <Alert\r\n message=\"Error\"\r\n description={error}\r\n type=\"error\"\r\n showIcon\r\n style={{ marginBottom: 16 }}\r\n />\r\n )}\r\n\r\n {apiInfo && (\r\n <div>\r\n <Alert\r\n message=\"Integration Guide\"\r\n description=\"Copy these URLs and use them in GraphXR to connect to your database.\"\r\n type=\"info\"\r\n showIcon\r\n style={{ marginBottom: 16 }}\r\n />\r\n\r\n {apiInfo.api_urls.info && \r\n renderUrlInput('Database Info API', apiInfo.api_urls.info, true, true)\r\n }\r\n\r\n {apiInfo.api_urls.query && \r\n renderUrlInput('Query API', apiInfo.api_urls.query)\r\n }\r\n\r\n {apiInfo.api_urls.schema && \r\n renderUrlInput('Schema API', apiInfo.api_urls.schema, true)\r\n }\r\n\r\n {apiInfo.api_urls.graphSchema && \r\n renderUrlInput('Graph Schema API', apiInfo.api_urls.graphSchema, true)\r\n }\r\n\r\n {apiInfo.api_urls.sampleData && \r\n renderUrlInput('Sample Data API', apiInfo.api_urls.sampleData, true)\r\n }\r\n\r\n\r\n {apiInfo.version && (\r\n <Card size=\"small\" style={{ marginTop: 16 }}>\r\n <Text strong>API Version: </Text>\r\n <Text code>{apiInfo.version}</Text>\r\n </Card>\r\n )}\r\n\r\n <Card size=\"small\" style={{ marginTop: 16 }}>\r\n <Title level={5}>Usage Example</Title>\r\n <pre style={{ \r\n backgroundColor: '#f5f5f5', \r\n padding: '12px', \r\n borderRadius: '4px',\r\n fontSize: '12px'\r\n }}>\r\n{`// Execute a query\r\ncurl -X POST ${getFullUrl(apiInfo.api_urls.query || '')} \\\\\r\n -H \"Content-Type: application/json\" \\\\\r\n -d '{\r\n \"query\": \"SELECT * FROM Users LIMIT 10\",\r\n \"parameters\": {}\r\n }'`}\r\n </pre>\r\n </Card>\r\n </div>\r\n )}\r\n </Modal>\r\n );\r\n};\r\n\r\nexport default ApiUrlsModal;","import React from 'react';\r\nimport { Card, Button, Typography, Space } from 'antd';\r\nimport { PlusOutlined } from '@ant-design/icons';\r\nimport { useNavigate } from 'react-router-dom';\r\n\r\nconst { Title } = Typography;\r\n\r\nconst ProjectListHeader: React.FC = () => {\r\n const navigate = useNavigate();\r\n\r\n return (\r\n <Card>\r\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>\r\n <Title level={2} style={{ margin: 0 }}>\r\n Database Projects\r\n </Title>\r\n <Space>\r\n <Button \r\n type=\"primary\" \r\n icon={<PlusOutlined />}\r\n onClick={() => navigate('/addProject')}\r\n size=\"large\"\r\n >\r\n Create New Project\r\n </Button>\r\n </Space>\r\n </div>\r\n </Card>\r\n );\r\n};\r\n\r\nexport default ProjectListHeader;","import React, { useState, useEffect } from 'react';\r\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\r\nimport {\r\n Card,\r\n Button,\r\n Table,\r\n Space,\r\n Typography,\r\n message,\r\n Modal,\r\n Popconfirm,\r\n Tag,\r\n Tooltip,\r\n} from 'antd';\r\nimport {\r\n CopyOutlined,\r\n EditOutlined,\r\n DeleteOutlined,\r\n LinkOutlined,\r\n ExclamationCircleOutlined,\r\n} from '@ant-design/icons';\r\nimport type { ColumnsType } from 'antd/es/table';\r\nimport { Project, DatabaseType } from '../types/project';\r\nimport { projectService } from '../services/projectService';\r\nimport ProjectForm from '../components/ProjectForm';\r\nimport ApiUrlsModal from '../components/ApiUrlsModal';\r\nimport ProjectListHeader from '../components/ProjectListHeader';\r\n\r\nconst handleCopyApiUrl = async (project: Project) => {\r\n try {\r\n const apiUrl = `${window.location.origin}/api/${project?.database_type}/${project.name}`;\r\n await navigator.clipboard.writeText(apiUrl);\r\n message.success('API URL copied to clipboard');\r\n } catch (error) {\r\n message.error('Failed to copy API URL');\r\n console.error('Error copying API URL:', error);\r\n }\r\n};\r\n\r\nconst ProjectManagement: React.FC = () => {\r\n const navigate = useNavigate();\r\n const location = useLocation();\r\n const { projectId } = useParams<{ projectId: string }>();\r\n \r\n const [projects, setProjects] = useState<Project[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n const [selectedProject, setSelectedProject] = useState<Project | null>(null);\r\n\r\n // 根据路由确定当前状态\r\n const isCreateMode = location.pathname === '/addProject';\r\n const isEditMode = location.pathname.startsWith('/editProject/');\r\n const createModalVisible = isCreateMode;\r\n const editModalVisible = isEditMode;\r\n const [apiUrlsModalVisible, setApiUrlsModalVisible] = useState(false);\r\n\r\n useEffect(() => {\r\n loadProjects();\r\n }, []);\r\n\r\n // 当路由变为编辑模式时,查找对应的项目\r\n useEffect(() => {\r\n if (isEditMode && projectId && projects.length > 0) {\r\n const project = projects.find(p => p.id === projectId);\r\n if (project) {\r\n setSelectedProject(project);\r\n } else {\r\n message.error('Project not found');\r\n navigate('/listProjects');\r\n }\r\n }\r\n }, [isEditMode, projectId, projects, navigate]);\r\n\r\n const loadProjects = async () => {\r\n setLoading(true);\r\n try {\r\n const data = await projectService.getProjects();\r\n setProjects(data);\r\n } catch (error) {\r\n message.error('Failed to load projects');\r\n console.error('Error loading projects:', error);\r\n } finally {\r\n setLoading(false);\r\n }\r\n };\r\n\r\n\r\n const handleEditProject = (project: Project) => {\r\n setSelectedProject(project);\r\n navigate(`/editProject/${project.id}`);\r\n };\r\n\r\n const handleDeleteProject = async (projectId: string) => {\r\n try {\r\n await projectService.deleteProject(projectId);\r\n message.success('Project deleted successfully');\r\n loadProjects();\r\n } catch (error) {\r\n message.error('Failed to delete project');\r\n console.error('Error deleting project:', error);\r\n }\r\n };\r\n\r\n const handleShowApiUrls = (project: Project) => {\r\n setSelectedProject(project);\r\n setApiUrlsModalVisible(true);\r\n };\r\n\r\n const handleCreateSubmit = async (values: any) => {\r\n try {\r\n await projectService.createProject(values);\r\n message.success('Project created successfully');\r\n navigate('/listProjects');\r\n loadProjects();\r\n } catch (error) {\r\n message.error('Failed to create project');\r\n console.error('Error creating project:', error);\r\n }\r\n };\r\n\r\n const handleEditSubmit = async (values: any) => {\r\n if (!selectedProject) return;\r\n \r\n try {\r\n await projectService.updateProject(selectedProject.id, values);\r\n message.success('Project updated successfully');\r\n navigate('/listProjects');\r\n loadProjects();\r\n } catch (error) {\r\n message.error('Failed to update project');\r\n console.error('Error updating project:', error);\r\n }\r\n };\r\n\r\n const handleModalCancel = () => {\r\n navigate('/listProjects');\r\n setSelectedProject(null);\r\n };\r\n\r\n const getDatabaseTypeColor = (type: DatabaseType): string => {\r\n switch (type) {\r\n case 'spanner':\r\n return 'blue';\r\n case 'postgresql':\r\n return 'green';\r\n case 'mysql':\r\n return 'orange';\r\n case 'mongodb':\r\n return 'purple';\r\n default:\r\n return 'default';\r\n }\r\n };\r\n\r\n const columns: ColumnsType<Project> = [\r\n {\r\n title: 'Project Name',\r\n dataIndex: 'name',\r\n key: 'name',\r\n render: (text: string) => <strong>{text}</strong>,\r\n },\r\n {\r\n title: 'Database Type',\r\n dataIndex: 'database_type',\r\n key: 'database_type',\r\n render: (type: DatabaseType) => (\r\n <Tag color={getDatabaseTypeColor(type)}>\r\n {type.toUpperCase()}\r\n </Tag>\r\n ),\r\n },\r\n {\r\n title: 'Created Time',\r\n dataIndex: 'create_time',\r\n key: 'create_time',\r\n render: (time: string) => new Date(time).toLocaleString(),\r\n },\r\n {\r\n title: 'Updated Time',\r\n dataIndex: 'update_time',\r\n key: 'update_time',\r\n render: (time: string) => new Date(time).toLocaleString(),\r\n },\r\n {\r\n title: 'Actions',\r\n key: 'actions',\r\n render: (_, record: Project) => (\r\n <Space size=\"small\">\r\n <Tooltip title=\"Copy the API URL (GraphXR uses it)\">\r\n <Button\r\n type=\"text\"\r\n icon={<CopyOutlined />}\r\n onClick={() => handleCopyApiUrl(record)}\r\n />\r\n </Tooltip>\r\n <Tooltip title=\"Show API URLs\">\r\n <Button\r\n type=\"text\"\r\n icon={<LinkOutlined />}\r\n onClick={() => handleShowApiUrls(record)}\r\n />\r\n </Tooltip>\r\n <Tooltip title=\"Edit Project\">\r\n <Button\r\n type=\"text\"\r\n icon={<EditOutlined />}\r\n onClick={() => handleEditProject(record)}\r\n />\r\n </Tooltip>\r\n <Popconfirm\r\n title=\"Delete Project\"\r\n description=\"Are you sure you want to delete this project?\"\r\n icon={<ExclamationCircleOutlined style={{ color: 'red' }} />}\r\n onConfirm={() => handleDeleteProject(record.id)}\r\n okText=\"Yes\"\r\n cancelText=\"No\"\r\n >\r\n <Tooltip title=\"Delete Project\">\r\n <Button\r\n type=\"text\"\r\n danger\r\n icon={<DeleteOutlined />}\r\n />\r\n </Tooltip>\r\n </Popconfirm>\r\n </Space>\r\n ),\r\n },\r\n ];\r\n\r\n return (\r\n <div className=\"project-management\">\r\n {/* 只在列表页面显示项目列表 */}\r\n {!isCreateMode && !isEditMode && (\r\n <>\r\n <ProjectListHeader />\r\n <Card style={{ marginTop: 16 }}>\r\n <Table\r\n columns={columns}\r\n dataSource={projects}\r\n rowKey=\"id\"\r\n loading={loading}\r\n pagination={{\r\n pageSize: 10,\r\n showSizeChanger: true,\r\n showQuickJumper: true,\r\n showTotal: (total, range) =>\r\n `${range[0]}-${range[1]} of ${total} items`,\r\n }}\r\n />\r\n </Card>\r\n </>\r\n )}\r\n\r\n {/* Create Project Modal */}\r\n <Modal\r\n title=\"Create New Project\"\r\n open={createModalVisible}\r\n onCancel={handleModalCancel}\r\n footer={null}\r\n width={800}\r\n >\r\n <ProjectForm\r\n onSubmit={handleCreateSubmit}\r\n onCancel={handleModalCancel}\r\n />\r\n </Modal>\r\n\r\n {/* Edit Project Modal */}\r\n <Modal\r\n title=\"Edit Project\"\r\n open={editModalVisible}\r\n onCancel={handleModalCancel}\r\n footer={null}\r\n width={800}\r\n >\r\n <ProjectForm\r\n initialValues={selectedProject}\r\n onSubmit={handleEditSubmit}\r\n onCancel={handleModalCancel}\r\n />\r\n </Modal>\r\n\r\n {/* API URLs Modal */}\r\n <ApiUrlsModal\r\n visible={apiUrlsModalVisible}\r\n project={selectedProject}\r\n onClose={() => setApiUrlsModalVisible(false)}\r\n />\r\n </div>\r\n );\r\n};\r\n\r\nexport default ProjectManagement;","import React from 'react';\r\nimport { Routes, Route } from 'react-router-dom';\r\nimport { Layout } from 'antd';\r\nimport AppHeader from './components/AppHeader';\r\nimport ProjectManagement from './pages/ProjectManagement';\r\nimport './styles/App.css';\r\n\r\nconst { Content } = Layout;\r\n\r\nconst App: React.FC = () => {\r\n return (\r\n <Layout className=\"app-layout\">\r\n <AppHeader />\r\n <Content className=\"app-content\">\r\n <div className=\"content-wrapper\">\r\n <Routes>\r\n <Route path=\"/\" element={<ProjectManagement />} />\r\n <Route path=\"/listProjects\" element={<ProjectManagement />} />\r\n <Route path=\"/addProject\" element={<ProjectManagement />} />\r\n <Route path=\"/editProject/:projectId\" element={<ProjectManagement />} />\r\n <Route path=\"/admin\" element={<ProjectManagement />} />\r\n <Route path=\"/projects\" element={<ProjectManagement />} />\r\n </Routes>\r\n </div>\r\n </Content>\r\n </Layout>\r\n );\r\n};\r\n\r\nexport default App;","import React from 'react';\r\nimport ReactDOM from 'react-dom/client';\r\nimport { BrowserRouter } from 'react-router-dom';\r\nimport { ConfigProvider } from 'antd';\r\nimport App from './App';\r\nimport './styles/index.css';\r\n\r\nconst root = ReactDOM.createRoot(\r\n document.getElementById('root') as HTMLElement\r\n);\r\n\r\nroot.render(\r\n <React.StrictMode>\r\n <ConfigProvider\r\n theme={{\r\n token: {\r\n colorPrimary: '#1890ff',\r\n borderRadius: 6,\r\n },\r\n }}\r\n >\r\n <BrowserRouter>\r\n <App />\r\n </BrowserRouter>\r\n </ConfigProvider>\r\n </React.StrictMode>\r\n);","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t792: 0\n};\n\n// no chunk on demand loading\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n__webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0);\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar [chunkIds, moreModules, runtime] = data;\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\treturn __webpack_require__.O(result);\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunkgraphxr_database_proxy_frontend\"] = self[\"webpackChunkgraphxr_database_proxy_frontend\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","// startup\n// Load entry module and return exports\n// This entry module depends on other loaded chunks and execution need to be delayed\nvar __webpack_exports__ = __webpack_require__.O(undefined, [96], () => (__webpack_require__(2465)))\n__webpack_exports__ = __webpack_require__.O(__webpack_exports__);\n"],"names":["deferred","leafPrototypes","getProto","Header","Layout","Title","Typography","AppHeader","navigate","useNavigate","location","useLocation","menuItems","key","icon","React","HomeOutlined","label","UnorderedListOutlined","PlusOutlined","className","Space","align","DatabaseOutlined","level","style","color","margin","Menu","theme","mode","selectedKeys","pathname","items","onClick","e","flex","justifyContent","Button","type","ApiOutlined","handleDocsClick","window","open","api","axios","create","baseURL","timeout","headers","interceptors","request","use","config","error","Promise","reject","response","console","projectService","getProjects","get","data","getProject","id","concat","createProject","project","post","updateProject","put","deleteProject","delete","getDatabaseInfo","databaseType","projectName","executeQuery","queryRequest","getSchema","getGraphSchema","getSampleData","testConnection","Option","Select","DefaultAuthType","origin","_ref","_projectsList$find","_databases$find","initialValues","onSubmit","onCancel","form","Form","useForm","setDatabaseType","authType","setAuthType","serviceAccountKey","setServiceAccountKey","loading","setLoading","listProjects","listDatabases","projectsList","setProjectsList","databases","setDatabases","useEffect","setFieldsValue","_objectSpread","name","database_type","database_config","auth_type","updateServiceAccountKey","oauth_config","isNewOauth2","token","isNewServiceAccount","private_key_id","async","fetch","method","body","JSON","stringify","auth","ok","projects","json","message","getProjectsList","project_id","instance_id","getDatabaseList","newData","newService","prevKey","database_id","graph_name","layout","onFinish","values","dbConfigFields","_objectWithoutProperties","_excluded","databaseConfig","options","redirect_uri","max_sessions","read_only","Row","gutter","Col","span","Item","rules","required","Input","placeholder","onChange","value","resetFields","Card","title","size","marginBottom","email","width","handleGoogleLogin","hostname","popup","screen","height","timer","setInterval","checkGoogleLoginDataWrited","localStorage","getItem","state","_popup","clearInterval","close","handleGoogleLoginData","refresh_token","expires_in","removeItem","last_refreshed","Date","now","log","Upload","customRequest","file","onSuccess","onError","reader","FileReader","onload","_e$target","content","target","result","jsonContent","parse","private_key","client_email","success","Error","onerror","readAsText","onRemove","handleRemoveServiceAccount","maxCount","accept","showUploadList","showPreviewIcon","showDownloadIcon","UploadOutlined","marginTop","disabled","showSearch","notFoundContent","filterOption","input","option","_String","String","children","toLowerCase","includes","map","proj","_String2","find","p","instances","inst","_String3","db","_String4","getFieldValue","graphDBs","graphDB","htmlType","Text","visible","onClose","apiInfo","setApiInfo","useState","setError","copiedUrl","setCopiedUrl","loadApiInfo","info","err","getFullUrl","relativePath","baseUrl","renderUrlInput","isGet","arguments","length","undefined","isForGraphXR","fullUrl","isCopied","strong","Compact","readOnly","CheckOutlined","CopyOutlined","url","navigator","clipboard","writeText","setTimeout","copyToClipboard","Modal","footer","textAlign","padding","Spin","Alert","description","showIcon","api_urls","query","schema","graphSchema","sampleData","version","code","backgroundColor","borderRadius","fontSize","ProjectListHeader","display","alignItems","ProjectManagement","projectId","useParams","setProjects","selectedProject","setSelectedProject","isCreateMode","isEditMode","startsWith","createModalVisible","editModalVisible","apiUrlsModalVisible","setApiUrlsModalVisible","loadProjects","handleModalCancel","getDatabaseTypeColor","columns","dataIndex","render","text","Tag","toUpperCase","time","toLocaleString","_","record","Tooltip","apiUrl","handleCopyApiUrl","LinkOutlined","EditOutlined","handleEditProject","Popconfirm","ExclamationCircleOutlined","onConfirm","handleDeleteProject","okText","cancelText","danger","DeleteOutlined","Table","dataSource","rowKey","pagination","pageSize","showSizeChanger","showQuickJumper","showTotal","total","range","ProjectForm","ApiUrlsModal","Content","App","Routes","Route","path","element","ReactDOM","document","getElementById","ConfigProvider","colorPrimary","BrowserRouter","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","exports","module","__webpack_modules__","m","O","chunkIds","fn","priority","notFulfilled","Infinity","i","fulfilled","j","Object","keys","every","splice","r","n","getter","__esModule","d","a","getPrototypeOf","obj","t","this","then","ns","def","current","indexOf","getOwnPropertyNames","forEach","definition","o","defineProperty","enumerable","g","globalThis","Function","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","installedChunks","chunkId","webpackJsonpCallback","parentChunkLoadingFunction","moreModules","runtime","some","chunkLoadingGlobal","self","bind","push","__webpack_exports__"],"sourceRoot":""}
|