Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
SQLITE_PATH=./data
JWT_TOKEN=waline-dev-jwt-token

# PostgreSQL example
POSTGRES_DATABASE=
POSTGRES_USER=
POSTGRES_PASSWORD=
POSTGRES_HOST=
PORSTGRES_PORT=
POSTGRES_PORT=
POSTGRES_PREFIX=
POSTGRES_SSL=
POSTGRES_SSL=
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ dist/

# npm config file
.npmrc

# other
data/
error.log
4 changes: 3 additions & 1 deletion docs/src/advanced/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ order: -1

::: tip

为了使 `@waline/server` 能在本地正常运行,你需要配置必要的本地环境变量至 `.env`。
当你没有配置任何存储环境变量时,`pnpm run server:dev` 会默认回退到 `./data` 下的本地 SQLite 数据库。

如果你想在本地使用其他存储后端,请在 `.env` 中配置对应的环境变量。

在 `.env.example` 我们准备了示例供你参考

Expand Down
4 changes: 3 additions & 1 deletion docs/src/en/advanced/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ If you want to contribute to waline, here is a guide.

::: tip

In order to run `@waline/server` locally, you need to configure some local environment variables to `.env`.
`pnpm run server:dev` will fall back to a local SQLite database at `./data` when no storage environment variables are set.

If you want to use another storage backend locally, configure the required environment variables in `.env`.

We provide an example for you in `.env.example`.

Expand Down
3 changes: 2 additions & 1 deletion packages/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Waline Management System</title>
<script>
window.serverURL = 'http://localhost:9090/api/';
window.serverURL = '/api/';
window.oauthServices = [];
</script>
</head>
<script type="module" src="./src/index.jsx"></script>
Expand Down
107 changes: 61 additions & 46 deletions packages/admin/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import Register from './pages/register/index.jsx';
import User from './pages/user/index.jsx';
import { store } from './store/index.js';
import { getUiPath } from './utils/ui.js';

const Access = (props) => {
const user = useSelector((state) => state.user);
Expand All @@ -18,15 +19,20 @@
const meta = props.meta ?? {};
const basename = props.basename ?? '';
const emptyUser = !user?.objectId;
const currentPath = location.pathname.replace(basename, '') || '/';
const redirectPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`;
const buildPath = (path) => `${basename}${path}`.replaceAll(/\/+/gu, '/');

if (emptyUser) {
return (location.href = `${basename}/ui/login?redirect=${location.pathname.replace(basename, '')}`);
location.href = `${buildPath(getUiPath('login'))}?redirect=${redirectPath}`;
return;
}

const noPermission = meta.auth ? props.meta.auth !== user.type : false;
const noPermission = meta.auth ? meta.auth !== user.type : false;

if (noPermission) {
return (location.href = `${basename}/ui/profile`);
location.href = buildPath(getUiPath('profile'));
return;

Check warning on line 35 in packages/admin/src/App.jsx

View workflow job for this annotation

GitHub Actions / test (24, ubuntu-latest)

eslint(no-useless-return)

Unnecessary return statement.

Check warning on line 35 in packages/admin/src/App.jsx

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

eslint(no-useless-return)

Unnecessary return statement.
}
}, [user, props.meta, props.basename]);

Expand All @@ -36,52 +42,61 @@
export default function App() {
const match = location.pathname.match(/(.*?)\/ui/u);
const basePath = match ? match[1] : '/';
const homePath = getUiPath();
const userPath = getUiPath('user');
const migrationPath = getUiPath('migration');
const loginPath = getUiPath('login');
const registerPath = getUiPath('register');
const forgotPath = getUiPath('forgot');
const profilePath = getUiPath('profile');

return (
<Provider store={store}>
<Router basename={basePath}>
<Routes>
<Route
path="/ui"
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<ManageComments />
</Access>
}
/>
<Route
path="/ui/user"
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<User />
</Access>
}
/>
<Route
path="/ui/migration"
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<Migration />
</Access>
}
/>
<Route path="/ui/login" exact element={<Login />} />
<Route path="/ui/register" exact element={<Register />} />
<Route path="/ui/forgot" exact element={<Forgot />} />
<Route
path="/ui/profile"
exact
element={
<Access>
<Profile />
</Access>
}
/>
</Routes>
</Router>
<div className="waline-admin-app">
<Router basename={basePath}>
<Routes>
<Route
path={homePath}
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<ManageComments />
</Access>
}
/>
<Route
path={userPath}
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<User />
</Access>
}
/>
<Route
path={migrationPath}
exact
element={
<Access meta={{ auth: 'administrator' }} basename={basePath}>
<Migration />
</Access>
}
/>
<Route path={loginPath} exact element={<Login />} />
<Route path={registerPath} exact element={<Register />} />
<Route path={forgotPath} exact element={<Forgot />} />
<Route
path={profilePath}
exact
element={
<Access>
<Profile />
</Access>
}
/>
</Routes>
</Router>
</div>
</Provider>
);
}
107 changes: 65 additions & 42 deletions packages/admin/src/components/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import { Link, useNavigate } from 'react-router';

import { LANGUAGE_OPTIONS } from '../locales/index.js';
import { buildAvatar } from '../pages/manage-comments/utils.js';
import { getUiPath } from '../utils/ui.js';

export default function Header() {
const dispatch = useDispatch();
Expand Down Expand Up @@ -42,54 +44,75 @@
const onLogout = (event) => {
event.preventDefault();
dispatch.user.logout();
navigate('/ui/login');
navigate(getUiPath('login'));
};

const siteName = window.SITE_NAME || 'Waline';

const navItems = [
{ to: getUiPath(), label: t('comment') },
{ to: getUiPath('user'), label: t('user') },
{ to: getUiPath('migration'), label: t('migration') },
];

return [
<div className="typecho-head-nav clear-fix" role="navigation" key="header">
{user?.type === 'administrator' ? (
<nav id="typecho-nav-list">
<ul className="root">
<li className="parent">
<Link to="/ui">{t('management')}</Link>
</li>
<ul className="child">
<li className="last">
<Link to="/ui">{t('comment')}</Link>
</li>
<li className="last">
<Link to="/ui/user">{t('user')}</Link>
</li>
<li className="last">
<Link to="/ui/migration">{t('migration')}</Link>
</li>
</ul>
</ul>
</nav>
) : null}
<div className="operate">
<div className="language-select">
<select defaultValue={defaultLanguage} onChange={updateLanguage} style={{ width: 120 }}>
{LANGUAGE_OPTIONS.map((options) => (
<option key={options.value} value={options.value}>
{options.label}
</option>
))}
</select>
</div>
{user?.type ? (
<Link to="/ui/profile" className="author">
{user.display_name}
<header className="typecho-head-nav clear-fix" role="navigation" key="header">

Check warning on line 59 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (24, ubuntu-latest)

jsx-a11y(prefer-tag-over-role)

Prefer `nav` over `role` attribute `navigation`.

Check warning on line 59 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

jsx-a11y(prefer-tag-over-role)

Prefer `nav` over `role` attribute `navigation`.
<div className="waline-admin-header">
<div className="waline-admin-brand">
<Link to={getUiPath()} className="waline-admin-brand-link">

Check warning on line 62 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (24, ubuntu-latest)

react(forbid-component-props)

Prop "className" is forbidden on Components

Check warning on line 62 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

react(forbid-component-props)

Prop "className" is forbidden on Components
<span className="waline-admin-brand-mark">W</span>
<span className="waline-admin-brand-copy">
<strong>{siteName}</strong>
<small>{t('management')}</small>
</span>
</Link>
) : null}
</div>

{user?.type === 'administrator' ? (
<nav id="typecho-nav-list" className="waline-admin-nav">
<ul className="root">
{navItems.map((item) => (
<li className="parent" key={item.to}>
<Link to={item.to}>{item.label}</Link>
</li>
))}
</ul>
</nav>
) : (
<div className="waline-admin-nav waline-admin-nav-static">
<span>{t('management')}</span>
</div>
)}

{user?.type ? (
<button type="button" className="exit" onClick={onLogout}>
{t('logout')}
</button>
) : null}
<div className="operate">
<div className="language-select">
<select defaultValue={defaultLanguage} onChange={updateLanguage}>
{LANGUAGE_OPTIONS.map((options) => (
<option key={options.value} value={options.value}>
{options.label}
</option>
))}
</select>
</div>
{user?.type ? (
<Link to={getUiPath('profile')} className="author">

Check warning on line 98 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (24, ubuntu-latest)

react(forbid-component-props)

Prop "className" is forbidden on Components

Check warning on line 98 in packages/admin/src/components/Header.jsx

View workflow job for this annotation

GitHub Actions / test (22, ubuntu-latest)

react(forbid-component-props)

Prop "className" is forbidden on Components
<img
className="author-avatar"
src={buildAvatar(user.display_name, user.email, user.avatar)}
alt={user.display_name || 'Waline'}
/>
<span className="author-name">{user.display_name}</span>
</Link>
) : null}

{user?.type ? (
<button type="button" className="exit" onClick={onLogout}>
{t('logout')}
</button>
) : null}
</div>
</div>
</div>,
</header>,
latestVersion ? (
<div className="upgrade-tips clear-fix" key="upgrade">
<Trans
Expand Down
53 changes: 45 additions & 8 deletions packages/admin/src/components/icon/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading