diff --git a/web/web/public/icons/git-fork.svg b/web/web/public/icons/git-fork.svg new file mode 100644 index 00000000000..0a1d7806591 --- /dev/null +++ b/web/web/public/icons/git-fork.svg @@ -0,0 +1,20 @@ + + + diff --git a/web/web/public/icons/github-mark.svg b/web/web/public/icons/github-mark.svg new file mode 100644 index 00000000000..13ad84ec591 --- /dev/null +++ b/web/web/public/icons/github-mark.svg @@ -0,0 +1,20 @@ + + + diff --git a/web/web/src/app/rootLayout/AppBar.js b/web/web/src/app/rootLayout/AppBar.js index c4917d58eee..1851b6bc405 100644 --- a/web/web/src/app/rootLayout/AppBar.js +++ b/web/web/src/app/rootLayout/AppBar.js @@ -41,6 +41,7 @@ import clsx from 'clsx' import VersionView from './VersionView' import LogoutButton from './Logout' +import GitHubInfo from './GitHubInfo' import { useSearchParams } from 'next/navigation' import { useRouter } from 'next/navigation' import { useAppSelector, useAppDispatch } from '@/lib/hooks/useStore' @@ -94,6 +95,7 @@ const AppBar = () => { > Gravitino + @@ -135,7 +137,7 @@ const AppBar = () => { ) : null} - + diff --git a/web/web/src/app/rootLayout/GitHubInfo.js b/web/web/src/app/rootLayout/GitHubInfo.js new file mode 100644 index 00000000000..50744302b9d --- /dev/null +++ b/web/web/src/app/rootLayout/GitHubInfo.js @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Link from 'next/link' +import Image from 'next/image' +import { Box, Typography } from '@mui/material' +import { Star } from '@mui/icons-material' +import { useAppSelector } from '@/lib/hooks/useStore' + +const GitHubInfo = () => { + const githubUrl = 'https://github.com/apache/gravitino' + const githubForkUrl = 'https://github.com/apache/gravitino/fork' + const githubLogoUrl = (process.env.NEXT_PUBLIC_BASE_PATH ?? '') + '/icons/github-mark.svg' + const forkLogoUrl = (process.env.NEXT_PUBLIC_BASE_PATH ?? '') + '/icons/git-fork.svg' + const store = useAppSelector(state => state.sys) + + return ( + + + logo + + + + + logo + {store.forks} Forks + + + + + + {store.stars} Stars + + + + + ) +} + +export default GitHubInfo diff --git a/web/web/src/app/rootLayout/VersionView.js b/web/web/src/app/rootLayout/VersionView.js index 70ba6990398..0f3d39b357d 100644 --- a/web/web/src/app/rootLayout/VersionView.js +++ b/web/web/src/app/rootLayout/VersionView.js @@ -26,7 +26,7 @@ const VersionView = () => { const store = useAppSelector(state => state.sys) return ( - + {store.version} ) diff --git a/web/web/src/lib/api/github/index.js b/web/web/src/lib/api/github/index.js new file mode 100644 index 00000000000..456316221d0 --- /dev/null +++ b/web/web/src/lib/api/github/index.js @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { defHttp } from '@/lib/utils/axios' + +const githubApis = { + GET: 'https://api.github.com/repos/apache/gravitino' +} + +export const getGitHubApi = () => { + return defHttp.get({ + url: `${githubApis.GET}`, + headers: { + Accept: 'application/vnd.github+json' + } + }) +} diff --git a/web/web/src/lib/provider/session.js b/web/web/src/lib/provider/session.js index 1c9858f9965..5247987c24e 100644 --- a/web/web/src/lib/provider/session.js +++ b/web/web/src/lib/provider/session.js @@ -25,7 +25,7 @@ import { useRouter } from 'next/navigation' import { useSearchParams } from 'next/navigation' import { useAppDispatch } from '@/lib/hooks/useStore' -import { initialVersion } from '@/lib/store/sys' +import { initialVersion, fetchGitHubInfo } from '@/lib/store/sys' import { to } from '../utils' import { getAuthConfigs, setAuthToken } from '../store/auth' @@ -78,11 +78,13 @@ const AuthProvider = ({ children }) => { if (authType === 'simple') { dispatch(initialVersion()) + dispatch(fetchGitHubInfo()) goToMetalakeListPage() } else if (authType === 'oauth') { if (token) { dispatch(setAuthToken(token)) dispatch(initialVersion()) + dispatch(fetchGitHubInfo()) goToMetalakeListPage() } else { router.push('/login') diff --git a/web/web/src/lib/store/sys/index.js b/web/web/src/lib/store/sys/index.js index 475063f8a76..cb7eb605041 100644 --- a/web/web/src/lib/store/sys/index.js +++ b/web/web/src/lib/store/sys/index.js @@ -19,10 +19,12 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' -import { loggerVersion, to } from '@/lib/utils' +import { loggerVersion, to, formatNumber, loggerGitHubInfo } from '@/lib/utils' import { getVersionApi } from '@/lib/api/version' +import { getGitHubApi } from '@/lib/api/github' + export const initialVersion = createAsyncThunk('sys/fetchVersion', async (params, { getState }) => { let version = null const [err, res] = await to(getVersionApi()) @@ -39,23 +41,54 @@ export const initialVersion = createAsyncThunk('sys/fetchVersion', async (params return version }) +export const fetchGitHubInfo = createAsyncThunk('sys/fetchGitHubInfo', async (params, { getState }) => { + let stars = 0 + let forks = 0 + const [err, res] = await to(getGitHubApi()) + if (err || !res) { + console.error('Error fetching repository status : ', err) + } + + stars = formatNumber(res.stargazers_count) + forks = formatNumber(res.forks_count) + loggerGitHubInfo(stars, forks) + + return { + stars: stars, + forks: forks + } +}) + export const sysSlice = createSlice({ name: 'sys', initialState: { - version: '' + version: '', + stars: 0, + forks: 0 }, reducers: { setVersion(state, action) { state.version = action.payload + }, + setStars(state, action) { + state.stars = action.payload + }, + setForks(state, action) { + state.forks = action.payload } }, extraReducers: builder => { - builder.addCase(initialVersion.fulfilled, (state, action) => { - state.version = action.payload.version - }) + builder + .addCase(initialVersion.fulfilled, (state, action) => { + state.version = action.payload.version + }) + .addCase(fetchGitHubInfo.fulfilled, (state, action) => { + state.stars = action.payload.stars + state.forks = action.payload.forks + }) } }) -export const { setVersion } = sysSlice.actions +export const { setVersion, setStars, setForks } = sysSlice.actions export default sysSlice.reducer diff --git a/web/web/src/lib/utils/index.js b/web/web/src/lib/utils/index.js index 17444dfa80e..8b41db98a67 100644 --- a/web/web/src/lib/utils/index.js +++ b/web/web/src/lib/utils/index.js @@ -45,6 +45,13 @@ export const loggerVersion = version => { ) } +export const loggerGitHubInfo = (stars, forks) => { + console.log( + `Gravitino GitHubInfo: %c Stars ${stars}, Forks ${forks}`, + `color: white; background-color: #6062E0; padding: 2px; border-radius: 4px;` + ) +} + export const genUpdates = (originalData, newData) => { const updates = [] @@ -227,3 +234,13 @@ export const findInTree = (tree, key, value) => { return result } + +export const formatNumber = num => { + if (num >= 1000000) { + return (num / 1000000).toFixed(1).replace(/\.0$/, '') + 'm' + } else if (num >= 1000) { + return (num / 1000).toFixed(1).replace(/\.0$/, '') + 'k' + } + + return num.toString() +}