Skip to content

feat: support edge-functions, identities, rpc, storage, and access co… #6750

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
5 changes: 5 additions & 0 deletions .changeset/shy-emus-smell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@refinedev/supabase": major
---

support new data providers for Supabase such as edge functions, storage, identities, rpc. We provide support for more complex auth provider behaviors.
6 changes: 6 additions & 0 deletions examples/with-react-router-7-framework-mode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/.cache
/build
/public/build
.env
43 changes: 43 additions & 0 deletions examples/with-react-router-7-framework-mode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<div align="center" style="margin: 30px;">
<a href="https://refine.dev/">
<img alt="refine logo" src="https://refine.ams3.cdn.digitaloceanspaces.com/readme/refine-readme-banner.png">
</a>

</br>
</br>

<div align="center">
<a href="https://refine.dev">Home Page</a> |
<a href="https://discord.gg/refine">Discord</a> |
<a href="https://refine.dev/examples/">Examples</a> |
<a href="https://refine.dev/blog/">Blog</a> |
<a href="https://refine.dev/docs/">Documentation</a>
</div>
</div>

</br>
</br>

<div align="center"><strong>Build your <a href="https://reactjs.org/">React</a>-based CRUD applications, without constraints.</strong><br>An open source, headless web application framework developed with flexibility in mind.

<br />
<br />

[![Discord](https://img.shields.io/discord/837692625737613362.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/refine)
[![Twitter Follow](https://img.shields.io/twitter/follow/refine_dev?style=social)](https://twitter.com/refine_dev)

<a href="https://www.producthunt.com/posts/refine-3?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-refine&#0045;3" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-badge.svg?post_id=362220&theme=light&period=daily" alt="refine - 100&#0037;&#0032;open&#0032;source&#0032;React&#0032;framework&#0032;to&#0032;build&#0032;web&#0032;apps&#0032;3x&#0032;faster | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>

</div>

## Try this example on your local

```bash
npm create refine-app@latest -- --example with-remix-material-ui
```

## Try this example on CodeSandbox

<br/>

[![Open with-remix-material-ui example from refine](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/embed/github/refinedev/refine/tree/master/examples/with-remix-material-ui?view=preview&theme=dark&codemirror=1)
164 changes: 164 additions & 0 deletions examples/with-react-router-7-framework-mode/app/authProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import type { AuthProvider } from "@refinedev/core";
import Cookies from "js-cookie";
import * as cookie from "cookie";

const mockUsers = [
{
email: "[email protected]",
roles: ["admin"],
},
{
email: "[email protected]",
roles: ["editor"],
},
{
email: "[email protected]",
roles: ["user"],
},
];

const COOKIE_NAME = "user";

export const authProvider: AuthProvider = {
login: async ({ email }) => {
// Suppose we actually send a request to the back end here.
const user = mockUsers.find((item) => item.email === email);

if (user) {
Cookies.set(COOKIE_NAME, JSON.stringify(user));
return {
success: true,
redirectTo: "/",
};
}

return {
success: false,
error: {
message: "Login failed",
name: "Invalid email or password",
},
};
},
register: async (params) => {
// Suppose we actually send a request to the back end here.
const user = mockUsers.find((item) => item.email === params.email);

if (user) {
Cookies.set(COOKIE_NAME, JSON.stringify(user));
return {
success: true,
redirectTo: "/",
};
}
return {
success: false,
error: {
message: "Register failed",
name: "Invalid email or password",
},
};
},
forgotPassword: async (params) => {
// Suppose we actually send a request to the back end here.
const user = mockUsers.find((item) => item.email === params.email);

if (user) {
//we can send email with reset password link here
return {
success: true,
};
}
return {
success: false,
error: {
message: "Forgot password failed",
name: "Invalid email",
},
};
},
updatePassword: async (params) => {
// Suppose we actually send a request to the back end here.
const isPasswordInvalid = params.password === "123456" || !params.password;

if (isPasswordInvalid) {
return {
success: false,
error: {
message: "Update password failed",
name: "Invalid password",
},
};
}

return {
success: true,
};
},
logout: async () => {
Cookies.remove(COOKIE_NAME);

return {
success: true,
redirectTo: "/login",
};
},
onError: async (error) => {
if (error && error.statusCode === 401) {
return {
error: new Error("Unauthorized"),
logout: true,
redirectTo: "/login",
};
}

return {};
},
check: async (request) => {
let user = undefined;
if (request) {
const hasCookie = request.headers.get("Cookie");
if (hasCookie) {
const parsedCookie = cookie.parse(request.headers.get("Cookie"));
user = parsedCookie[COOKIE_NAME];
}
} else {
const parsedCookie = Cookies.get(COOKIE_NAME);
user = parsedCookie ? JSON.parse(parsedCookie) : undefined;
}

const { pathname } = new URL(request.url);

const query = pathname === "/" ? "" : `?to=${encodeURIComponent(pathname)}`;

if (!user) {
return {
authenticated: false,
error: {
message: "Check failed",
name: "Unauthorized",
},
logout: true,
redirectTo: `/login${query}`,
};
}

return {
authenticated: true,
};
},
getPermissions: async () => {
return null;
},
getIdentity: async () => {
const cookie = Cookies.get(COOKIE_NAME);
if (!cookie) return null;

return {
id: 1,
name: "Jane Doe",
avatar:
"https://unsplash.com/photos/IWLOvomUmWU/download?force=true&w=640",
};
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
import AppBar from "@mui/material/AppBar";
import Avatar from "@mui/material/Avatar";
import IconButton from "@mui/material/IconButton";
import Stack from "@mui/material/Stack";
import Toolbar from "@mui/material/Toolbar";
import Typography from "@mui/material/Typography";
import { useGetIdentity } from "@refinedev/core";
import {
HamburgerMenu,
type RefineThemedLayoutV2HeaderProps,
} from "@refinedev/mui";
import React, { useContext } from "react";
import { ColorModeContext } from "~/contexts/ColorModeContext";

type IUser = {
id: number;
name: string;
avatar: string;
};

export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = () => {
const { mode, setMode } = useContext(ColorModeContext);

const { data: user } = useGetIdentity<IUser>();

return (
<AppBar color="default" position="sticky">
<Toolbar>
<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<HamburgerMenu />

<Stack
direction="row"
width="100%"
justifyContent="flex-end"
alignItems="center"
>
<IconButton
onClick={() => {
setMode();
}}
>
{mode === "dark" ? <LightModeOutlined /> : <DarkModeOutlined />}
</IconButton>

{(user?.avatar || user?.name) && (
<Stack
direction="row"
gap="16px"
alignItems="center"
justifyContent="center"
>
{user?.name && (
<Typography variant="subtitle2">{user?.name}</Typography>
)}
<Avatar src={user?.avatar} alt={user?.name} />
</Stack>
)}
</Stack>
</Stack>
</Toolbar>
</AppBar>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const API_URL = "https://api.fake-rest.refine.dev";
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from "react";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";

export interface ClientStyleContextData {
reset: () => void;
}

interface ClientCacheProviderProps {
children: React.ReactNode;
}

export const ClientStyleContext = React.createContext<ClientStyleContextData>({
// eslint-disable-next-line @typescript-eslint/no-empty-function
reset: () => {},
});

const createEmotionCache = () => {
return createCache({ key: "css" });
};

export const ClientStyleCacheProvider = ({
children,
}: ClientCacheProviderProps) => {
const [cache, setCache] = React.useState(createEmotionCache());

const clientStyleContextValue = React.useMemo(
() => ({
reset() {
setCache(createEmotionCache());
},
}),
[],
);

return (
<ClientStyleContext.Provider value={clientStyleContextValue}>
<CacheProvider value={cache}>{children}</CacheProvider>
</ClientStyleContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import useMediaQuery from "@mui/material/useMediaQuery";
import { ThemeProvider } from "@mui/material/styles";
import { RefineThemes } from "@refinedev/mui";
import { parseCookies, setCookie } from "nookies";
import React, {
type PropsWithChildren,
createContext,
useEffect,
useState,
} from "react";

type ColorModeContextType = {
mode: string;
setMode: () => void;
};

export const ColorModeContext = createContext<ColorModeContextType>(
{} as ColorModeContextType,
);

export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({
children,
}) => {
const [isMounted, setIsMounted] = useState(false);
const [mode, setMode] = useState("light");

useEffect(() => {
setIsMounted(true);
}, []);

const systemTheme = useMediaQuery("(prefers-color-scheme: dark)");

useEffect(() => {
if (isMounted) {
setMode(parseCookies()["theme"] || (systemTheme ? "dark" : "light"));
}
}, [isMounted, systemTheme]);

const toggleTheme = () => {
const nextTheme = mode === "light" ? "dark" : "light";

setMode(nextTheme);
setCookie(null, "theme", nextTheme);
};

return (
<ColorModeContext.Provider
value={{
setMode: toggleTheme,
mode,
}}
>
<ThemeProvider
// you can change the theme colors here. example: mode === "light" ? RefineThemes.Magenta : RefineThemes.MagentaDark
theme={mode === "light" ? RefineThemes.Blue : RefineThemes.BlueDark}
>
{children}
</ThemeProvider>
</ColorModeContext.Provider>
);
};
Loading