feat: add public visibility

This commit is contained in:
Steven 2022-09-14 23:08:24 +08:00
parent 5a79304153
commit 469c841713
9 changed files with 81 additions and 27 deletions

View File

@ -4,6 +4,8 @@ package api
type Visibility string type Visibility string
const ( const (
// VisibilityPublic is the PUBLIC visibility.
VisibilityPublic Visibility = "PUBLIC"
// VisibilityWorkspace is the WORKSPACE visibility. // VisibilityWorkspace is the WORKSPACE visibility.
VisibilityWorkspace Visibility = "WORKSPACE" VisibilityWorkspace Visibility = "WORKSPACE"
// VisibilityPrivite is the PRIVATE visibility. // VisibilityPrivite is the PRIVATE visibility.
@ -12,6 +14,8 @@ const (
func (e Visibility) String() string { func (e Visibility) String() string {
switch e { switch e {
case VisibilityPublic:
return "PUBLIC"
case VisibilityWorkspace: case VisibilityWorkspace:
return "WORKSPACE" return "WORKSPACE"
case VisibilityPrivite: case VisibilityPrivite:

View File

@ -60,7 +60,9 @@ func aclMiddleware(s *Server, next echo.HandlerFunc) echo.HandlerFunc {
return next(c) return next(c)
} }
if common.HasPrefixes(path, "/api/ping", "/api/status", "/api/user/:id") && c.Request().Method == http.MethodGet { println("path", path)
if common.HasPrefixes(path, "/api/ping", "/api/status", "/api/user/:id", "/api/workspace/:workspaceName/shortcut/:shortcutName") && c.Request().Method == http.MethodGet {
return next(c) return next(c)
} }

View File

@ -92,11 +92,6 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) {
g.GET("/workspace/:workspaceName/shortcut/:shortcutName", func(c echo.Context) error { g.GET("/workspace/:workspaceName/shortcut/:shortcutName", func(c echo.Context) error {
ctx := c.Request().Context() ctx := c.Request().Context()
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
workspaceName := c.Param("workspaceName") workspaceName := c.Param("workspaceName")
shortcutName := c.Param("shortcutName") shortcutName := c.Param("shortcutName")
if workspaceName == "" || shortcutName == "" { if workspaceName == "" || shortcutName == "" {
@ -113,20 +108,6 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find workspace with name %s", workspaceName)).SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find workspace with name %s", workspaceName)).SetInternal(err)
} }
workspaceUser, err := s.Store.FindWordspaceUser(ctx, &api.WorkspaceUserFind{
WorkspaceID: &workspace.ID,
UserID: &userID,
})
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, "workspace user not found")
}
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find workspace user").SetInternal(err)
}
if workspaceUser == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "not workspace user")
}
shortcut, err := s.Store.FindShortcut(ctx, &api.ShortcutFind{ shortcut, err := s.Store.FindShortcut(ctx, &api.ShortcutFind{
WorkspaceID: &workspace.ID, WorkspaceID: &workspace.ID,
Name: &shortcutName, Name: &shortcutName,
@ -138,6 +119,27 @@ func (s *Server) registerWorkspaceRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find shortcut with name %s", shortcutName)).SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to find shortcut with name %s", shortcutName)).SetInternal(err)
} }
if shortcut.Visibility != api.VisibilityPublic {
userID, ok := c.Get(getUserIDContextKey()).(int)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "Missing user in session")
}
workspaceUser, err := s.Store.FindWordspaceUser(ctx, &api.WorkspaceUserFind{
WorkspaceID: &workspace.ID,
UserID: &userID,
})
if err != nil {
if common.ErrorCode(err) == common.NotFound {
return echo.NewHTTPError(http.StatusNotFound, "workspace user not found")
}
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find workspace user").SetInternal(err)
}
if workspaceUser == nil {
return echo.NewHTTPError(http.StatusUnauthorized, "not workspace user")
}
}
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8) c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(shortcut)); err != nil { if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(shortcut)); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode shortcut response").SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, "Failed to encode shortcut response").SetInternal(err)

View File

@ -116,7 +116,7 @@ CREATE TABLE shortcut (
name TEXT NOT NULL, name TEXT NOT NULL,
link TEXT NOT NULL DEFAULT '', link TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '',
visibility TEXT NOT NULL CHECK (visibility IN ('PRIVATE', 'WORKSPACE')) DEFAULT 'PRIVATE', visibility TEXT NOT NULL CHECK (visibility IN ('PRIVATE', 'WORKSPACE', 'PUBLIC')) DEFAULT 'PRIVATE',
FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE, FOREIGN KEY(creator_id) REFERENCES user(id) ON DELETE CASCADE,
FOREIGN KEY(workspace_id) REFERENCES workspace(id) ON DELETE CASCADE FOREIGN KEY(workspace_id) REFERENCES workspace(id) ON DELETE CASCADE
); );

View File

@ -9,6 +9,6 @@ VALUES
( (
11, 11,
101, 101,
'Demo', 'demo',
'' ''
); );

View File

@ -16,3 +16,22 @@ VALUES
'百度搜索', '百度搜索',
'WORKSPACE' 'WORKSPACE'
); );
INSERT INTO
shortcut (
`creator_id`,
`workspace_id`,
`name`,
`link`,
`description`,
`visibility`
)
VALUES
(
102,
11,
'bl',
'https://bilibili.com',
'B站',
'PUBLIC'
);

View File

@ -1,5 +1,5 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { Route, Routes, useNavigate } from "react-router-dom"; import { Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { userService, workspaceService } from "./services"; import { userService, workspaceService } from "./services";
import useLoading from "./hooks/useLoading"; import useLoading from "./hooks/useLoading";
import Only from "./components/common/OnlyWhen"; import Only from "./components/common/OnlyWhen";
@ -9,11 +9,27 @@ import WorkspaceDetail from "./pages/WorkspaceDetail";
import UserDetail from "./pages/UserDetail"; import UserDetail from "./pages/UserDetail";
import ShortcutRedirector from "./pages/ShortcutRedirector"; import ShortcutRedirector from "./pages/ShortcutRedirector";
const pathnameWhitelist = [/\/.+?\/go\/.+/];
function App() { function App() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation();
const pageLoadingStatus = useLoading(); const pageLoadingStatus = useLoading();
useEffect(() => { useEffect(() => {
let needAuth = true;
for (const regexp of pathnameWhitelist) {
if (regexp.test(location.pathname)) {
needAuth = false;
break;
}
}
if (!needAuth) {
pageLoadingStatus.setFinish();
return;
}
userService.initialState().finally(() => { userService.initialState().finally(() => {
if (!userService.getState().user) { if (!userService.getState().user) {
pageLoadingStatus.setFinish(); pageLoadingStatus.setFinish();

View File

@ -148,7 +148,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
checked={state.shortcutCreate.visibility === "PRIVATE"} checked={state.shortcutCreate.visibility === "PRIVATE"}
/> />
<label htmlFor="visibility-private" className="ml-1 mr-4"> <label htmlFor="visibility-private" className="ml-1 mr-4">
Only for myself Private
</label> </label>
<input <input
type="radio" type="radio"
@ -158,8 +158,19 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
onChange={handleVisibilityInputChange} onChange={handleVisibilityInputChange}
checked={state.shortcutCreate.visibility === "WORKSPACE"} checked={state.shortcutCreate.visibility === "WORKSPACE"}
/> />
<label htmlFor="visibility-workspace" className="ml-1"> <label htmlFor="visibility-workspace" className="ml-1 mr-4">
Public in workspace Workspace
</label>
<input
type="radio"
name="visibility"
id="visibility-public"
value="PUBLIC"
onChange={handleVisibilityInputChange}
checked={state.shortcutCreate.visibility === "PUBLIC"}
/>
<label htmlFor="visibility-public" className="ml-1">
Public
</label> </label>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
type ShortcutId = number; type ShortcutId = number;
type Visibility = "PRIVATE" | "WORKSPACE"; type Visibility = "PRIVATE" | "WORKSPACE" | "PUBLIC";
interface Shortcut { interface Shortcut {
id: ShortcutId; id: ShortcutId;