This is an automated email from the ASF dual-hosted git repository.

jialiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ambari-website.git


The following commit(s) were added to refs/heads/main by this push:
     new 48b0f79  AMBARI-26497: Website: Team page avatar can not fetch #37
48b0f79 is described below

commit 48b0f799c42ee780bb934bda064b44cd21268d80
Author: zrain <[email protected]>
AuthorDate: Tue May 13 08:46:15 2025 +0800

    AMBARI-26497: Website: Team page avatar can not fetch #37
---
 .github/workflows/website.yml |   6 +-
 package.json                  |   3 +-
 scripts/updateTeamAvatar.js   |  92 ++++++++++++++++++++++
 src/pages/team/index.js       |  94 +++++++++++------------
 src/pages/team/index.less     | 174 +++++++++++++++++++++++-------------------
 src/pages/team/languages.json |  10 +--
 6 files changed, 244 insertions(+), 135 deletions(-)

diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index e290332..8b0677a 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -2,7 +2,7 @@ name: Build and Deploy
 
 permissions:
   contents: write
-  
+
 on:
   pull_request:
     branches:
@@ -25,7 +25,7 @@ jobs:
           node-version: 18.12.1
 
       - name: Build Main Document
-        run: yarn && yarn build
+        run: yarn && yarn update-avatar && yarn build && git checkout .
 
       - name: Deploy
         if: ${{ github.event_name == 'push' }}
@@ -54,4 +54,4 @@ jobs:
           git commit -m '.asf.yaml'
           git push origin asf-site
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/package.json b/package.json
index 03727c6..ad526c4 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,8 @@
     "serve": "docusaurus serve",
     "write-translations": "docusaurus write-translations",
     "write-heading-ids": "docusaurus write-heading-ids",
-    "typecheck": "tsc"
+    "typecheck": "tsc",
+    "update-avatar": "node scripts/updateTeamAvatar.js"
   },
   "config": {
     "commitizen": {
diff --git a/scripts/updateTeamAvatar.js b/scripts/updateTeamAvatar.js
new file mode 100644
index 0000000..0ac6de7
--- /dev/null
+++ b/scripts/updateTeamAvatar.js
@@ -0,0 +1,92 @@
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+const { promisify } = require('util');
+
+const readFile = promisify(fs.readFile);
+const writeFile = promisify(fs.writeFile);
+const mkdir = promisify(fs.mkdir);
+
+const AVATAR_JSON_PATH = path.resolve(__dirname, 
'../src/pages/team/languages.json');
+const OUTPUT_DIR = './static/img/team';
+const RELATIVE_PATH_PREFIX = '/img/team/';
+
+async function ensureDirectoryExists(dirPath) {
+  try {
+    await mkdir(dirPath, { recursive: true });
+  } catch (err) {
+    if (err.code !== 'EEXIST') throw err;
+  }
+}
+
+async function downloadImage(url, filePath) {
+  return new Promise((resolve, reject) => {
+    const file = fs.createWriteStream(filePath);
+
+    https.get(url, (response) => {
+      if (response.statusCode !== 200) {
+        reject(new Error(`Request error, status code: 
${response.statusCode}`));
+        return;
+      }
+
+      const contentType = response.headers['content-type'];
+      if (!contentType || !contentType.startsWith('image/')) {
+        reject(new Error(`Invalid content type: ${contentType}`));
+        return;
+      }
+
+      response.pipe(file);
+
+      file.on('finish', () => {
+        file.close(resolve);
+      });
+    }).on('error', (err) => {
+      fs.unlink(filePath, () => { });
+      reject(err);
+    });
+  });
+}
+
+
+async function processAvatars() {
+  try {
+    await ensureDirectoryExists(OUTPUT_DIR);
+    const data = await readFile(AVATAR_JSON_PATH, 'utf8');
+    const jsonData = JSON.parse(data);
+
+    if (jsonData.en && jsonData.en.pmc) await 
avatarSectionProcess(jsonData.en.pmc)
+    if (jsonData.en && jsonData.en.committer) await 
avatarSectionProcess(jsonData.en.committer)
+
+    await writeFile(AVATAR_JSON_PATH, JSON.stringify(jsonData, null, 2));
+    console.log('JSON update!');
+  } catch (err) {
+    console.error('Error cause:', err);
+  }
+}
+
+async function avatarSectionProcess(avatarList) {
+  if (!Array.isArray(avatarList)) {
+    throw new Error('Invalid json');
+  }
+
+  for (let i = 0; i < avatarList.length; i++) {
+    const item = avatarList[i];
+    if (!item.avatarUrl) continue;
+
+    try {
+      const url = item.avatarUrl;
+      const ext = path.extname(url) || '.png';
+      const filename = `avatar_${Date.now()}_${i}${ext}`;
+      const outputPath = path.join(OUTPUT_DIR, filename);
+
+      await downloadImage(url, outputPath);
+
+      item.avatarUrl = RELATIVE_PATH_PREFIX + filename;
+      console.log(`Download success: ${url} -> ${item.avatarUrl}`);
+    } catch (err) {
+      console.error(`Download failed ${item.avatarUrl}: ${err.message}`);
+    }
+  }
+}
+
+processAvatars();
diff --git a/src/pages/team/index.js b/src/pages/team/index.js
index b19e483..1965e32 100644
--- a/src/pages/team/index.js
+++ b/src/pages/team/index.js
@@ -5,53 +5,53 @@ import Layout from '@theme/Layout';
 import './index.less';
 
 export default function () {
-    const isBrowser = useIsBrowser();
-    const language = isBrowser && location.pathname.indexOf('/zh-CN/') === 0 ? 
'zh-CN' : 'en';
-    const dataSource = config?.[language];
+  const isBrowser = useIsBrowser();
+  const language = isBrowser && location.pathname.indexOf('/zh-CN/') === 0 ? 
'zh-CN' : 'en';
+  const dataSource = config?.[language];
 
-    return (
-        <Layout title="team" description="team page">
-            <div className="container">
-            <div className="block team_page">
-                <h3 className="team_title">Ambari Team</h3>
-                <p className="team_desc" dangerouslySetInnerHTML={ { __html: 
dataSource.info.desc } }/>
-                <h3 className="team_title">PMC</h3>
-                <p className="team_desc">{dataSource.info.tip}</p>
-                <ul className="character_list">
-                    {
-                        dataSource.pmc.map((item, i) => (
-                            <a href={'https://github.com/' + item.githubId} 
key={i} target="_blank">
-                                <li className="character_item text_center" 
style={{'listStyle': 'none'}}>
-                                    <img className="character_avatar" 
src={item.avatarUrl} alt={item.name}/>
-                                    <div className="character_desc">
-                                        <h3 
className="character_name">{item.name}</h3>
-                                        <h3 className="character_id"><span 
className="githubId">githubId:</span>{item.githubId}</h3>
-                                    </div>
-                                </li>
-                            </a>
-                        ))
-                    }
-                </ul>
+  return (
+    <Layout title="team" description="team page">
+      <div className="container">
+        <div className="block team_page">
+          <h3 className="team_title">Ambari Team</h3>
+          <p className="team_desc" dangerouslySetInnerHTML={{ __html: 
dataSource.info.desc }} />
+          <h3 className="team_title">PMC</h3>
+          <p className="team_desc">{dataSource.info.tip}</p>
+          <ul className="character_list">
+            {
+              dataSource.pmc.map((item, i) => (
+                <li key={i} className="character_item text_center" style={{ 
'listStyle': 'none' }}>
+                  <a href={'https://github.com/' + item.githubId} 
target="_blank">
+                    <img className="character_avatar" src={item.avatarUrl} 
alt={item.name} />
+                    <div className="character_desc">
+                      <h3 className="character_name">{item.name}</h3>
+                      <h3 className="character_id"><span 
class="mdi--github"></span>{item.githubId}</h3>
+                    </div>
+                  </a>
+                </li>
+              ))
+            }
+          </ul>
 
-                <h3 className="team_title">Committers</h3>
-                <p className="team_desc">{dataSource.info.tip}</p>
-                <ul className="character_list">
-                  {
-                    dataSource.committer.map((item, i) => (
-                      <a href={'https://github.com/' + item.githubId} key={i} 
target="_blank">
-                        <li className="character_item text_center" 
style={{'listStyle': 'none'}}>
-                          <img className="character_avatar" 
src={item.avatarUrl} alt={item.name}/>
-                          <div className="character_desc">
-                            <h3 className="character_name">{item.name}</h3>
-                            <h3 className="character_id"><span 
className="githubId">githubId:</span>{item.githubId}</h3>
-                          </div>
-                        </li>
-                      </a>
-                    ))
-                  }
-                </ul>
-            </div>
-            </div>
-        </Layout>
-    );
+          <h3 className="team_title">Committers</h3>
+          <p className="team_desc">{dataSource.info.tip}</p>
+          <ul className="character_list">
+            {
+              dataSource.committer.map((item, i) => (
+                <li key={i} className="character_item text_center" style={{ 
'listStyle': 'none' }}>
+                  <a href={'https://github.com/' + item.githubId} 
target="_blank">
+                    <img className="character_avatar" src={item.avatarUrl} 
alt={item.name} />
+                    <div className="character_desc">
+                      <h3 className="character_name">{item.name}</h3>
+                      <h3 className="character_id"><span 
class="mdi--github"></span>{item.githubId}</h3>
+                    </div>
+                  </a>
+                </li>
+              ))
+            }
+          </ul>
+        </div>
+      </div>
+    </Layout>
+  );
 }
diff --git a/src/pages/team/index.less b/src/pages/team/index.less
index 26bf048..816b1f8 100644
--- a/src/pages/team/index.less
+++ b/src/pages/team/index.less
@@ -1,91 +1,107 @@
- @active-color: #3B84F6;//#2863f9
+ @active-color: #3B84F6; //#2863f9
  @enhance-color: #0F1222;
 
-.team_page {
+ .mdi--github {
+   display: inline-block;
+   width: 20px;
+   height: 20px;
+   --svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' 
viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 2A10 10 0 0 0 2 12c0 4.42 
2.87 8.17 6.84 
9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1
 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 
2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 
1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 
2.5-.33s1.71.11 2.5.33c1 [...]
+   background-color: currentColor;
+   -webkit-mask-image: var(--svg);
+   mask-image: var(--svg);
+   -webkit-mask-repeat: no-repeat;
+   mask-repeat: no-repeat;
+   -webkit-mask-size: 100% 100%;
+   mask-size: 100% 100%;
+ }
 
-  a {
-    text-decoration: none;
-  }
+ .team_page {
 
-  margin-top: 50px;
+   a {
+     text-decoration: none;
+   }
 
-  .team_title {
-    font-size: 25px;
-    font-weight: 500;
-    color: #0F1222;
-    margin-top: 50px;
-  }
+   margin-top: 50px;
 
-  .team_desc {
-    margin-bottom: 40px;
-  }
+   .team_title {
+     font-size: 25px;
+     font-weight: 500;
+     color: #0F1222;
+   }
 
-  .character_list {
-    display: grid;
-    grid-template-columns: repeat(6, 1fr);
-    grid-column-gap: 15px;
-    grid-row-gap: 15px;
-    padding: 20px 0 0px;
+   .team_desc {
+     margin-bottom: 24px;
+   }
 
-    .character_item {
-      border: 1px solid rgba(205, 221, 250, 0.8);
-      background-color: rgba(205, 222, 234, 0.2);
-      box-shadow: 0 3px 5px rgb(47 85 212 / 8%);
-      border-radius: 2px;
-      min-width: 0;
-      padding: 0 10px 5px;
-      margin: 40px 0 42px 0;
+   .character_list {
+     padding: 0;
+     margin: 0;
+     margin-bottom: 28px;
+     display: grid;
+     grid-template-columns: repeat(auto-fill, 200px);
+     grid-row-gap: 20px;
+     grid-column-gap: 20px;
 
-      .character_avatar {
-        width: 120px;
-        height: 120px;
-        background: #D8D8D8;
-        display: inline-block;
-        border-radius: 50%;
-        border: 5px solid rgba(255,255,255,.08);
-        margin: -4rem auto -.5rem;
-      }
+     .character_item {
+       margin-top: 30px;
+       position: relative;
+       min-width: 200px;
+       border-radius: 4px;
+       box-shadow: rgba(0, 0, 0, 0.02) 0px 1px 3px 0px, rgba(27, 31, 35, 0.15) 
0px 0px 0px 1px;
+       flex-shrink: 0;
 
-      .character_name {
-        color: @enhance-color;
-        line-height: 36px;
-        font-size: 15px;
-        white-space: nowrap;
-        font-weight: 400;
-        overflow: hidden;
-        background-color: #fff;
-        margin-top: 20px;
-        border-radius: 18px;
-        text-overflow: ellipsis;
-        border: 1px solid rgba(205, 221, 250, 0.6);
-        box-shadow: 0 3px 5px rgb(47 85 212 / 5%);
-      }
+       h3 {
+         margin-bottom: 0;
+       }
 
-      .character_id {
-        color: #333;
-        line-height: 36px;
-        font-size: 12px;
-        white-space: nowrap;
-        font-weight: 400;
-        overflow: hidden;
-        border-radius: 18px;
-        background-color: #fff;
-        text-overflow: ellipsis;
-        border: 1px solid rgba(205, 221, 250, 0.6);
-        box-shadow: 0 3px 5px rgb(47 85 212 / 5%);
-        .githubId {
-          color: #666;
-          margin-right: 2px;
-        }
-      }
+       .character_avatar {
+         position: absolute;
+         left: 8px;
+         top: -30px;
+         width: 60px;
+         height: 60px;
+         background: #D8D8D8;
+         display: inline-block;
+         border-radius: 50%;
+         box-shadow: rgba(0, 0, 0, 0.02) 0px 1px 3px 0px, rgba(27, 31, 35, 
0.15) 0px 0px 0px 1px;
+       }
 
-      .character_link {
-        color: rgba(15, 18, 34, 0.65);
-        font-weight: 400;
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-}
\ No newline at end of file
+       .character_name {
+         color: @enhance-color;
+         padding: 0 8px;
+         padding-top: 40px;
+         font-size: 15px;
+         white-space: nowrap;
+         font-weight: 400;
+         overflow: hidden;
+         text-overflow: ellipsis;
+         margin-bottom: 8px;
+       }
+
+       .character_id {
+         padding: 0 8px;
+         padding-bottom: 13px;
+         font-size: 12px;
+         color: #666;
+         font-weight: 400;
+         overflow: hidden;
+         white-space: nowrap;
+         text-overflow: ellipsis;
+
+         .mdi--github {
+           margin-right: 4px;
+           color: #333;
+           vertical-align: -6px;
+         }
+       }
+
+       .character_link {
+         color: rgba(15, 18, 34, 0.65);
+         font-weight: 400;
+         white-space: nowrap;
+         overflow: hidden;
+         text-overflow: ellipsis;
+       }
+     }
+   }
+ }
diff --git a/src/pages/team/languages.json b/src/pages/team/languages.json
index 1c0eef9..89092e6 100644
--- a/src/pages/team/languages.json
+++ b/src/pages/team/languages.json
@@ -148,9 +148,7 @@
         "gitUrl": "https://github.com/virajjasani";,
         "githubId": "virajjasani",
         "name": "Viraj Jasani"
-      }
-    ],
-    "committer": [
+      },
       {
         "apacheId": "jialiang",
         "avatarUrl": "https://avatars.githubusercontent.com/u/18082602?v=4";,
@@ -158,7 +156,9 @@
         "gitUrl": "https://github.com/Jialiangc";,
         "githubId": "Jialiangc",
         "name": "Jialiang Cai"
-      },
+      }
+    ],
+    "committer": [
       {
         "apacheId": "zhangyu",
         "avatarUrl": "https://avatars.githubusercontent.com/u/16508606?v=4";,
@@ -185,4 +185,4 @@
       }
     ]
   }
-}
\ No newline at end of file
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to