mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: remove patreon
This commit is contained in:
1 parent
da8025a0ad
commit
0df58d2d2c
13 files changed
+21
-326
No files matched your search
@@ -3,7 +3,6 @@
|
|||||||
import { idToRegion } from '@blitzkit/core';
|
import { idToRegion } from '@blitzkit/core';
|
||||||
import { Button, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
import { Button, Flex, Heading, Link, Text } from '@radix-ui/themes';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import type { PatreonAuthResponse } from '../pages/auth/[provider]/_index';
|
|
||||||
import { App } from '../stores/app';
|
import { App } from '../stores/app';
|
||||||
import { CURRENT_POLICIES_AGREEMENT_INDEX } from '../stores/app/constants';
|
import { CURRENT_POLICIES_AGREEMENT_INDEX } from '../stores/app/constants';
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ export function Checks() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Wargaming: 14 (refresh: 7)
|
* Wargaming: 14 (refresh: 7)
|
||||||
* Patreon: 31 (refresh: 15)
|
|
||||||
*/
|
*/
|
||||||
function Content() {
|
function Content() {
|
||||||
const logins = App.use((state) => state.logins);
|
const logins = App.use((state) => state.logins);
|
||||||
@@ -103,33 +101,6 @@ function Content() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logins.patreon) {
|
|
||||||
const expiresIn = logins.patreon.expires - Date.now();
|
|
||||||
const expiresInDays = expiresIn / 1000 / 60 / 60 / 24;
|
|
||||||
|
|
||||||
if (expiresInDays < 0) {
|
|
||||||
mutateApp((draft) => {
|
|
||||||
draft.logins.patreon = undefined;
|
|
||||||
});
|
|
||||||
} else if (expiresInDays < 15) {
|
|
||||||
const { patreon } = appStore.getState().logins;
|
|
||||||
|
|
||||||
if (!patreon) return;
|
|
||||||
|
|
||||||
fetch(`/api/patreon/refresh/${patreon.refreshToken}`)
|
|
||||||
.then((response) => response.json() as Promise<PatreonAuthResponse>)
|
|
||||||
.then((data) => {
|
|
||||||
mutateApp((draft) => {
|
|
||||||
draft.logins.patreon = {
|
|
||||||
token: data.access_token,
|
|
||||||
refreshToken: data.refresh_token,
|
|
||||||
expires: Date.now() + data.expires_in * 1000,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!showPoliciesAgreement) return null;
|
if (!showPoliciesAgreement) return null;
|
||||||
|
|||||||
@@ -84,8 +84,6 @@ const gameDefinitions = await fetchGameDefinitions();
|
|||||||
GitHub
|
GitHub
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/docs/patches" color="gray"> Patches </Link>
|
<Link href="/docs/patches" color="gray"> Patches </Link>
|
||||||
|
|
||||||
{/* patreon supporters? */}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { resolveBranchName } from '../core/blitzkit/branches';
|
import { resolveBranchName } from '../core/blitzkit/branches';
|
||||||
import { homeTool, TOOLS } from '../constants/tools';
|
import { homeTool, TOOLS } from '../constants/tools';
|
||||||
import { PatreonIcon } from './PatreonIcon';
|
|
||||||
import { NavbarAccount } from './NavbarAccount';
|
import { NavbarAccount } from './NavbarAccount';
|
||||||
|
|
||||||
const toolsFiltered = TOOLS.filter((tool) => !('href' in tool));
|
const toolsFiltered = TOOLS.filter((tool) => !('href' in tool));
|
||||||
@@ -176,15 +175,6 @@ const isBranchNamed = branchName !== undefined;
|
|||||||
>
|
>
|
||||||
<DiscordLogoIcon />
|
<DiscordLogoIcon />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
|
||||||
style={{ display: 'flex', alignContent: 'center' }}
|
|
||||||
color="gray"
|
|
||||||
href="https://www.patreon.com/tresabhi"
|
|
||||||
underline="none"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<PatreonIcon width="0.75em" height="0.75em" />
|
|
||||||
</Link>
|
|
||||||
<Link
|
<Link
|
||||||
style={{ display: 'flex', alignContent: 'center' }}
|
style={{ display: 'flex', alignContent: 'center' }}
|
||||||
color="gray"
|
color="gray"
|
||||||
|
|||||||
@@ -1,20 +1,15 @@
|
|||||||
import { patreonLoginUrl } from '@blitzkit/core';
|
|
||||||
import { PersonIcon } from '@radix-ui/react-icons';
|
import { PersonIcon } from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Flex,
|
Flex,
|
||||||
IconButton,
|
IconButton,
|
||||||
Inset,
|
|
||||||
Link,
|
|
||||||
Popover,
|
Popover,
|
||||||
Separator,
|
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Text,
|
Text,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
import { App } from '../../stores/app';
|
import { App } from '../../stores/app';
|
||||||
import { BlitzKitTheme } from '../BlitzKitTheme';
|
import { BlitzKitTheme } from '../BlitzKitTheme';
|
||||||
import { PatreonIcon } from '../PatreonIcon';
|
|
||||||
import { WargamingAccountName } from '../WargamingAccountName';
|
import { WargamingAccountName } from '../WargamingAccountName';
|
||||||
import { WargamingIcon } from '../WargamingIcon';
|
import { WargamingIcon } from '../WargamingIcon';
|
||||||
import { WargamingLoginButton } from '../WargamingLoginButton';
|
import { WargamingLoginButton } from '../WargamingLoginButton';
|
||||||
@@ -46,30 +41,12 @@ function Content() {
|
|||||||
width="320px"
|
width="320px"
|
||||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||||
>
|
>
|
||||||
{(logins.patreon || logins.wargaming) && (
|
{logins.wargaming && (
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Text align="center" color="gray">
|
<Text align="center" color="gray">
|
||||||
Logged in accounts
|
Logged in accounts
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{logins.patreon && (
|
|
||||||
<Flex align="center" gap="2" justify="center">
|
|
||||||
<PatreonIcon width={15} height={15} />
|
|
||||||
<Text>Patreon</Text>
|
|
||||||
<Button
|
|
||||||
color="red"
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
mutateApp((draft) => {
|
|
||||||
draft.logins.patreon = undefined;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Logout
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{logins.wargaming && (
|
{logins.wargaming && (
|
||||||
<Flex align="center" gap="2" justify="center">
|
<Flex align="center" gap="2" justify="center">
|
||||||
<WargamingIcon width={15} height={15} />
|
<WargamingIcon width={15} height={15} />
|
||||||
@@ -96,26 +73,18 @@ function Content() {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!logins.patreon !== !logins.wargaming && (
|
{/* {!logins. !== !logins.wargaming && (
|
||||||
<Inset side="x">
|
<Inset side="x">
|
||||||
<Separator my="4" size="4" />
|
<Separator my="4" size="4" />
|
||||||
</Inset>
|
</Inset>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{(!logins.patreon || !logins.wargaming) && (
|
{!logins.wargaming && (
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Text align="center" color="gray">
|
<Text align="center" color="gray">
|
||||||
Log in with...
|
Log in with...
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{!logins.patreon && (
|
|
||||||
<Link style={{ width: '100%' }} href={patreonLoginUrl()}>
|
|
||||||
<Button style={{ width: '100%' }} color="tomato">
|
|
||||||
<PatreonIcon width={15} height={15} /> Patreon
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!logins.wargaming && <WargamingLoginButton />}
|
{!logins.wargaming && <WargamingLoginButton />}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import type { ComponentProps } from 'react';
|
|
||||||
|
|
||||||
export function PatreonIcon(props: ComponentProps<'svg'>) {
|
|
||||||
return (
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436 476" {...props}>
|
|
||||||
<path
|
|
||||||
fill="currentColor"
|
|
||||||
data-fill="1"
|
|
||||||
d="M436 143c-.084-60.778-47.57-110.591-103.285-128.565C263.528-7.884 172.279-4.649 106.214 26.424 26.142 64.089.988 146.596.051 228.883c-.77 67.653 6.004 245.841 106.83 247.11 74.917.948 86.072-95.279 120.737-141.623 24.662-32.972 56.417-42.285 95.507-51.929C390.309 265.865 436.097 213.011 436 143Z"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { assertSecret, patreonLoginUrl } from '@blitzkit/core';
|
import { assertSecret } from '@blitzkit/core';
|
||||||
import { ArrowRightIcon } from '@radix-ui/react-icons';
|
import { ArrowRightIcon } from '@radix-ui/react-icons';
|
||||||
import { Button, Code, Flex, Link, Separator, Text } from '@radix-ui/themes';
|
import { Button, Code, Flex, Link, Text } from '@radix-ui/themes';
|
||||||
import { useAdExempt } from '../hooks/useAdExempt';
|
|
||||||
import { App } from '../stores/app';
|
import { App } from '../stores/app';
|
||||||
import { PatreonIcon } from './PatreonIcon';
|
|
||||||
|
|
||||||
export function Plugs() {
|
export function Plugs() {
|
||||||
return (
|
return (
|
||||||
@@ -14,9 +12,6 @@ export function Plugs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Content() {
|
function Content() {
|
||||||
const exempt = useAdExempt();
|
|
||||||
const hasPatreon = App.use((state) => state.logins.patreon !== undefined);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
align="center"
|
align="center"
|
||||||
@@ -28,50 +23,22 @@ function Content() {
|
|||||||
>
|
>
|
||||||
{assertSecret(import.meta.env.PUBLIC_PROMOTE_OPENTEST) === 'true' &&
|
{assertSecret(import.meta.env.PUBLIC_PROMOTE_OPENTEST) === 'true' &&
|
||||||
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH) !== 'opentest' && (
|
assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH) !== 'opentest' && (
|
||||||
<>
|
<Flex direction="column" gap="3" align="center">
|
||||||
<Flex direction="column" gap="3" align="center">
|
<Text size="5">
|
||||||
<Text size="5">
|
BlitzKit{' '}
|
||||||
BlitzKit{' '}
|
<Code variant="outline" size="4" color="gray" highContrast>
|
||||||
<Code variant="outline" size="4" color="gray" highContrast>
|
opentest
|
||||||
opentest
|
</Code>{' '}
|
||||||
</Code>{' '}
|
is available now{' '}
|
||||||
is available now{' '}
|
</Text>
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Link href="https://opentest.blitzkit.app/" target="_blank">
|
<Link href="https://opentest.blitzkit.app/" target="_blank">
|
||||||
<Button color="green">
|
<Button color="green">
|
||||||
BlitzKit opentest <ArrowRightIcon />
|
BlitzKit opentest <ArrowRightIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Separator size="3" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Flex direction="column" gap="3" align="center">
|
|
||||||
<Flex gap="1" align="center">
|
|
||||||
<Text size="4" align="center">
|
|
||||||
{exempt
|
|
||||||
? 'You are supporting BlitzKit, thank you!'
|
|
||||||
: 'Active Patreon supports go ad-free'}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="2" wrap="wrap" justify="center">
|
|
||||||
<Link href="https://www.patreon.com/tresabhi" target="_blank">
|
|
||||||
<Button>
|
|
||||||
<PatreonIcon width={15} height={15} />{' '}
|
|
||||||
{exempt ? 'Manage membership' : 'Support on Patreon'}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
{!hasPatreon && !exempt && (
|
|
||||||
<Link href={patreonLoginUrl()}>
|
|
||||||
<Button variant="outline">Log in with Patreon</Button>
|
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
</Flex>
|
||||||
</Flex>
|
)}
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,6 @@ import { times } from 'lodash-es';
|
|||||||
import { memo, useEffect, useMemo, useState } from 'react';
|
import { memo, useEffect, useMemo, useState } from 'react';
|
||||||
import { filterTank } from '../../core/blitzkit/filterTank';
|
import { filterTank } from '../../core/blitzkit/filterTank';
|
||||||
import { resolveReload } from '../../core/blitzkit/resolveReload';
|
import { resolveReload } from '../../core/blitzkit/resolveReload';
|
||||||
import { useAdExempt } from '../../hooks/useAdExempt';
|
|
||||||
import { $tankFilters } from '../../stores/tankFilters';
|
import { $tankFilters } from '../../stores/tankFilters';
|
||||||
import {
|
import {
|
||||||
SORT_NAMES,
|
SORT_NAMES,
|
||||||
@@ -49,7 +48,6 @@ const tankNames = await fetchTankNames();
|
|||||||
export const TankSearch = memo<TankSearchProps>(
|
export const TankSearch = memo<TankSearchProps>(
|
||||||
({ compact, onSelect, onSelectAll, ...props }) => {
|
({ compact, onSelect, onSelectAll, ...props }) => {
|
||||||
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
|
||||||
const exempt = useAdExempt();
|
|
||||||
const tankFilters = useStore($tankFilters);
|
const tankFilters = useStore($tankFilters);
|
||||||
const tankopediaSort = useStore($tankopediaSort);
|
const tankopediaSort = useStore($tankopediaSort);
|
||||||
|
|
||||||
|
|||||||
-2
@@ -26,7 +26,6 @@ import { Fragment } from 'react/jsx-runtime';
|
|||||||
import { lerp } from 'three/src/math/MathUtils.js';
|
import { lerp } from 'three/src/math/MathUtils.js';
|
||||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||||
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
import { tankCharacteristics } from '../../../../../core/blitzkit/tankCharacteristics';
|
||||||
import { useAdExempt } from '../../../../../hooks/useAdExempt';
|
|
||||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||||
import { Duel } from '../../../../../stores/duel';
|
import { Duel } from '../../../../../stores/duel';
|
||||||
@@ -39,7 +38,6 @@ const provisionDefinitions = await fetchProvisionDefinitions();
|
|||||||
|
|
||||||
export function Characteristics() {
|
export function Characteristics() {
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
const exempt = useAdExempt();
|
|
||||||
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
const crewSkills = TankopediaEphemeral.use((state) => state.skills);
|
||||||
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
const penetrationDistanceInput = useRef<HTMLInputElement>(null);
|
||||||
const hasImprovedVentilation = useEquipment(102);
|
const hasImprovedVentilation = useEquipment(102);
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { App } from '../stores/app';
|
|
||||||
|
|
||||||
let cache: Record<string, boolean | Promise<boolean>> = {};
|
|
||||||
|
|
||||||
export function useAdExempt() {
|
|
||||||
const patreon = App.use((state) => state.logins.patreon);
|
|
||||||
const [exempt, setExempt] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
if (!patreon) return setExempt(false);
|
|
||||||
|
|
||||||
if (!(patreon.token in cache)) {
|
|
||||||
cache[patreon.token] = new Promise<boolean>(async (resolve) => {
|
|
||||||
const response = await fetch(`/api/patreon/membership`, {
|
|
||||||
cache: 'default',
|
|
||||||
headers: { token: patreon.token },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) return resolve(false);
|
|
||||||
|
|
||||||
const json = await response.json();
|
|
||||||
const exempt = typeof json === 'boolean' && json;
|
|
||||||
|
|
||||||
cache[patreon.token] = exempt;
|
|
||||||
resolve(exempt);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setExempt(await cache[patreon.token]);
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return exempt;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import { assertSecret, type BlitzkitResponse } from '@blitzkit/core';
|
|
||||||
import type { APIContext } from 'astro';
|
|
||||||
|
|
||||||
export const prerender = false;
|
|
||||||
|
|
||||||
export async function GET({ request, params }: APIContext) {
|
|
||||||
const code = request.headers.get('code');
|
|
||||||
|
|
||||||
if (!code) {
|
|
||||||
return Response.json({
|
|
||||||
status: 'error',
|
|
||||||
error: 'UNDEFINED_CODE',
|
|
||||||
} satisfies BlitzkitResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
`https://www.patreon.com/api/oauth2/token?code=${
|
|
||||||
code
|
|
||||||
}&grant_type=authorization_code&client_id=${assertSecret(
|
|
||||||
import.meta.env.PUBLIC_PATREON_CLIENT_ID,
|
|
||||||
)}&client_secret=${assertSecret(
|
|
||||||
import.meta.env.PATREON_CLIENT_SECRET,
|
|
||||||
)}&redirect_uri=${assertSecret(
|
|
||||||
import.meta.env.PUBLIC_PATREON_REDIRECT_URI,
|
|
||||||
)}`,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const json = await response.json();
|
|
||||||
|
|
||||||
return Response.json(json);
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { assertSecret } from '@blitzkit/core';
|
|
||||||
import type { APIContext } from 'astro';
|
|
||||||
|
|
||||||
export const prerender = false;
|
|
||||||
|
|
||||||
interface PatreonMembershipResponse {
|
|
||||||
data: {
|
|
||||||
attributes: unknown;
|
|
||||||
id: `${number}`;
|
|
||||||
relationships: {
|
|
||||||
memberships: {
|
|
||||||
data: {
|
|
||||||
id: string; // uuid
|
|
||||||
type: 'member';
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
type: 'user';
|
|
||||||
};
|
|
||||||
included?: (
|
|
||||||
| {
|
|
||||||
attributes: {
|
|
||||||
patron_status:
|
|
||||||
| 'active_patron'
|
|
||||||
| 'declined_patron'
|
|
||||||
| 'former_patron'
|
|
||||||
| null; // null = never a paid member
|
|
||||||
};
|
|
||||||
id: string; // uuid
|
|
||||||
relationships: {
|
|
||||||
campaign: {
|
|
||||||
data: {
|
|
||||||
id: `${number}`; // id of my campaign to check against
|
|
||||||
type: 'campaign';
|
|
||||||
};
|
|
||||||
links: {
|
|
||||||
related: string; // url
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
type: 'member';
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
attributes: {};
|
|
||||||
id: `${number}`;
|
|
||||||
type: 'campaign'; // ignore this discriminator
|
|
||||||
}
|
|
||||||
)[];
|
|
||||||
links: {
|
|
||||||
self: 'https://www.patreon.com/api/oauth2/v2/user/124201389';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET({ request }: APIContext) {
|
|
||||||
const token = request.headers.get('token');
|
|
||||||
|
|
||||||
if (!token) return Response.json(false);
|
|
||||||
|
|
||||||
const patreonResponse = await fetch(
|
|
||||||
encodeURI(
|
|
||||||
'https://www.patreon.com/api/oauth2/v2/identity?include=memberships.campaign&fields[member]=patron_status',
|
|
||||||
),
|
|
||||||
{ headers: { Authorization: `Bearer ${token}` } },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!patreonResponse.ok) return Response.json(false);
|
|
||||||
|
|
||||||
const json = (await patreonResponse.json()) as PatreonMembershipResponse;
|
|
||||||
|
|
||||||
const response = Response.json(
|
|
||||||
json.included?.some(
|
|
||||||
(item) =>
|
|
||||||
item.type === 'member' &&
|
|
||||||
item.relationships.campaign.data.id ===
|
|
||||||
assertSecret(process.env.PATREON_CAMPAIGN_ID) &&
|
|
||||||
item.attributes.patron_status === 'active_patron',
|
|
||||||
) ?? false,
|
|
||||||
);
|
|
||||||
|
|
||||||
response.headers.set('Cache-Control', 'max-age=86400'); // a day
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,14 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { App } from '../../../stores/app';
|
import { App } from '../../../stores/app';
|
||||||
|
|
||||||
export const AUTH_PROVIDERS = ['wargaming', 'patreon'] as const;
|
export const AUTH_PROVIDERS = ['wargaming'] as const;
|
||||||
|
|
||||||
type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
type AuthProvider = (typeof AUTH_PROVIDERS)[number];
|
||||||
|
|
||||||
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
export const AUTH_PROVIDER_NAMES: Record<AuthProvider, string> = {
|
||||||
patreon: 'Patreon',
|
|
||||||
wargaming: 'Wargaming',
|
wargaming: 'Wargaming',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface PatreonAuthResponse {
|
|
||||||
access_token: string;
|
|
||||||
expires_in: number;
|
|
||||||
token_type: 'Bearer';
|
|
||||||
scope: string;
|
|
||||||
refresh_token: string;
|
|
||||||
version: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AuthorizeProps {
|
interface AuthorizeProps {
|
||||||
provider: AuthProvider;
|
provider: AuthProvider;
|
||||||
}
|
}
|
||||||
@@ -59,25 +49,6 @@ function Content({ provider }: AuthorizeProps) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'patreon': {
|
|
||||||
if (!searchParams.get('code')) break;
|
|
||||||
|
|
||||||
const response = await fetch('/api/patreon/auth', {
|
|
||||||
headers: { code: searchParams.get('code')! },
|
|
||||||
});
|
|
||||||
const data = (await response.json()) as PatreonAuthResponse;
|
|
||||||
|
|
||||||
mutateApp((draft) => {
|
|
||||||
draft.logins.patreon = {
|
|
||||||
token: data.access_token,
|
|
||||||
refreshToken: data.refresh_token,
|
|
||||||
expires: Date.now() + data.expires_in * 1000,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown provider: ${provider}`);
|
throw new Error(`Unknown provider: ${provider}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ interface AppStore {
|
|||||||
policiesAgreementIndex: number;
|
policiesAgreementIndex: number;
|
||||||
logins: {
|
logins: {
|
||||||
wargaming?: { token: string; id: number; expires: number };
|
wargaming?: { token: string; id: number; expires: number };
|
||||||
patreon?: { token: string; refreshToken: string; expires: number };
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user