mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-10 04:43:03 -05:00
feat: test auth
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import starlight from '@astrojs/starlight';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import svelte from '@astrojs/svelte';
|
||||
import vercel from '@astrojs/vercel/serverless';
|
||||
|
||||
export const locales = {
|
||||
root: {
|
||||
@@ -66,7 +67,7 @@ export default defineConfig({
|
||||
// ru: 'Leaderboard'
|
||||
// }
|
||||
// },
|
||||
{
|
||||
{
|
||||
label: 'Challenges',
|
||||
autogenerate: {
|
||||
directory: 'challenges'
|
||||
@@ -109,5 +110,7 @@ export default defineConfig({
|
||||
},
|
||||
defaultLocale: 'root',
|
||||
locales
|
||||
}), svelte()]
|
||||
}), svelte()],
|
||||
output: "hybrid",
|
||||
adapter: vercel()
|
||||
});
|
||||
|
||||
1559
docs/package-lock.json
generated
1559
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
||||
"dependencies": {
|
||||
"@astrojs/starlight": "^0.15.1",
|
||||
"@astrojs/svelte": "^5.2.0",
|
||||
"@astrojs/vercel": "^7.5.0",
|
||||
"@fontsource/ibm-plex-serif": "^5.0.8",
|
||||
"astro": "^4.0.0",
|
||||
"sharp": "^0.32.5",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getEntry } from 'astro:content';
|
||||
|
||||
const { lang } = Astro.props;
|
||||
const { data } = await getEntry('i18n', lang);
|
||||
|
||||
---
|
||||
|
||||
<div class="action-footer">
|
||||
@@ -37,7 +38,6 @@ const { data } = await getEntry('i18n', lang);
|
||||
text-decoration: none;
|
||||
font-size: var(--sl-text-sm) !important;
|
||||
border: 1px solid;
|
||||
font-size: var(--sl-text-base);
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let error = false;
|
||||
let loading = true;
|
||||
let stargazersCount = 0;
|
||||
let forksCount = 0;
|
||||
|
||||
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/repos/tomalaforge/angular-challenges`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const { stargazers_count, forks } = await response.json();
|
||||
stargazersCount = stargazers_count;
|
||||
forksCount = forks;
|
||||
|
||||
} catch (e) {
|
||||
error = true;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchStats();
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if !error && !loading}
|
||||
<div class="github">
|
||||
<a class="category" href="https://github.com/tomalaforge/angular-challenges" target="_blank">
|
||||
<slot name="star"/>
|
||||
<div>{stargazersCount}</div>
|
||||
</a>
|
||||
|
||||
<a class="category" href="https://github.com/tomalaforge/angular-challenges/fork" target="_blank">
|
||||
<slot name="fork"/>
|
||||
<div>{forksCount}</div>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.github {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-left: var(--sl-nav-gap);
|
||||
}
|
||||
|
||||
.category {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
gap: 0.25rem;
|
||||
color: var(--sl-color-text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.category:hover {
|
||||
color: var(--sl-color-accent-high);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,19 +1,20 @@
|
||||
---
|
||||
import Default from '@astrojs/starlight/components/SiteTitle.astro';
|
||||
import GitHubStats from './GitHubStats.svelte';
|
||||
import MyIcon from './MyIcon.astro';
|
||||
import SignUp from './github/SignUp.svelte';
|
||||
import { Icon } from '@astrojs/starlight/components';
|
||||
---
|
||||
|
||||
<Default {...Astro.props}>
|
||||
<slot />
|
||||
</Default>
|
||||
|
||||
<SignUp client:load />
|
||||
|
||||
<!--<GitHubStats client:load >-->
|
||||
<!-- <MyIcon name="star" slot="star" />-->
|
||||
<!-- <MyIcon name="fork" viewBox="0 0 16 16" slot="fork"/>-->
|
||||
<!--</GitHubStats>-->
|
||||
<SignUp client:only="svelte" >
|
||||
<Icon name="github" slot="github" />
|
||||
<MyIcon name="fullStar" slot="fullStar" viewBox="0 0 16 16" fill="#e3b341"/>
|
||||
<MyIcon name="star" slot="star" viewBox="0 0 16 16"/>
|
||||
<MyIcon name="fork" viewBox="0 0 16 16" slot="fork"/>
|
||||
</SignUp>
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { data, error, isLoaded, isLoading, token, totalCount } from './github-store';
|
||||
|
||||
export let challengeNumber;
|
||||
|
||||
let page = 1;
|
||||
|
||||
token.subscribe(token => {
|
||||
if (token) {
|
||||
fetchTotalCount();
|
||||
}
|
||||
})
|
||||
|
||||
async function fetchTotalCount() {
|
||||
isLoading.set(true);
|
||||
try {
|
||||
while (true) {
|
||||
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:"${challengeNumber}"+label:"answer"&per_page=100&page=${page}`);
|
||||
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:"${challengeNumber}"+label:"answer"&per_page=100&page=${page}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${$token}`,
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
throw new Error('Failed to fetch data');
|
||||
}
|
||||
const { items: new_items, total_count } = await response.json();
|
||||
if (!new_items || new_items.length === 0) break;
|
||||
@@ -30,13 +39,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchTotalCount();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
token: {$token}
|
||||
{#if $isLoaded}
|
||||
<div class="solution-container" id="answers">
|
||||
<div>Answered by</div>
|
||||
|
||||
144
docs/src/components/github/GitHubStats.svelte
Normal file
144
docs/src/components/github/GitHubStats.svelte
Normal file
@@ -0,0 +1,144 @@
|
||||
<script>
|
||||
import { token } from './github-store';
|
||||
|
||||
let error = false;
|
||||
let loading = true;
|
||||
let loadingStar = true;
|
||||
let stargazersCount = 0;
|
||||
let forksCount = 0;
|
||||
let isStarByUser = false;
|
||||
|
||||
token.subscribe(token => {
|
||||
if (token) {
|
||||
fetchStats();
|
||||
isStar();
|
||||
}
|
||||
});
|
||||
|
||||
async function starRepo() {
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/user/starred/tomalaforge/angular-challenges`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: `token ${$token}`
|
||||
}
|
||||
});
|
||||
if (response.ok) {
|
||||
isStarByUser = !isStarByUser;
|
||||
console.log('Starred', isStarByUser);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function isStar() {
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/user/starred/tomalaforge/angular-challenges`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `token ${$token}`
|
||||
}
|
||||
});
|
||||
if (response.ok && response.status === 204) {
|
||||
isStarByUser = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
loadingStar = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const response = await fetch(`https://api.github.com/repos/tomalaforge/angular-challenges`);
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
const refresh = await fetch('/auth/refresh');
|
||||
if (refresh.ok) {
|
||||
const data = await refresh.json();
|
||||
token.set(data.token);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to fetch data');
|
||||
}
|
||||
}
|
||||
const { stargazers_count, forks } = await response.json();
|
||||
stargazersCount = stargazers_count;
|
||||
forksCount = forks;
|
||||
|
||||
} catch (e) {
|
||||
error = true;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !error && !loading && !loadingStar}
|
||||
<div class="github">
|
||||
{#if isStarByUser}
|
||||
<div class="category starred">
|
||||
<slot name="fullStar" />
|
||||
<span>{stargazersCount}</span>
|
||||
</div>
|
||||
{:else}
|
||||
<button class="button-star link" on:click={starRepo}>
|
||||
<slot name="star" />
|
||||
<span>{stargazersCount}</span>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<a class="category link" href="https://github.com/tomalaforge/angular-challenges/fork" target="_blank">
|
||||
<slot name="fork" />
|
||||
<div>{forksCount}</div>
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
|
||||
.github {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-left: var(--sl-nav-gap);
|
||||
}
|
||||
|
||||
.button-star {
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
gap: 0.5em;
|
||||
align-items: center;
|
||||
border-radius: 999rem;
|
||||
color: var(--sl-color-white) !important;
|
||||
background-color: transparent;
|
||||
line-height: 1.1875;
|
||||
text-decoration: none;
|
||||
font-size: var(--sl-text-xs);
|
||||
border: 1px solid;
|
||||
padding: 0.2rem 0.4rem;
|
||||
margin-left: -6px
|
||||
}
|
||||
|
||||
.category {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
gap: 0.25rem;
|
||||
color: var(--sl-color-text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
color: var(--sl-color-accent-high);
|
||||
}
|
||||
|
||||
.starred {
|
||||
color: #e3b341;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,38 +1,61 @@
|
||||
<script>
|
||||
import { loadToken, test } from './github-store';
|
||||
import GitHubStats from './GitHubStats.svelte';
|
||||
import { loadToken, token } from './github-store';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
// Function to redirect the user to GitHub's signup page
|
||||
async function redirectToGitHubSignup() {
|
||||
// token.set('test-token');
|
||||
window.location.href = `/auth/authorize`;
|
||||
// await fetch(`/auth/authorize?redirect_uri=${window.location.href}&state=jsqhd&client_id=Iv1.711903007f608691`)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
loadToken();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const t = searchParams.get('token');
|
||||
if(t) {
|
||||
token.set(t);
|
||||
window.location.href = `${window.location.origin}${window.location.pathname}`;
|
||||
} else {
|
||||
loadToken();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{$test}
|
||||
<button on:click={redirectToGitHubSignup}>
|
||||
Sign Up for GitHub
|
||||
</button>
|
||||
{#if !$token}
|
||||
<a href={`/auth/authorize?redirect_uri=${window.location.href}`}>
|
||||
<slot name="github"/>
|
||||
<span class="github-sign-in">Sign in</span>
|
||||
</a>
|
||||
{:else}
|
||||
<GitHubStats>
|
||||
<slot name="fullStar" slot="fullStar"/>
|
||||
<slot name="star" slot="star"/>
|
||||
<slot name="fork" slot="fork"/>
|
||||
</GitHubStats>
|
||||
{/if}
|
||||
|
||||
|
||||
<style>
|
||||
button {
|
||||
background-color: #2ea44f;
|
||||
a {
|
||||
background-color: #238636;
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
padding: 8px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
border-radius: 5px;
|
||||
align-items: center;
|
||||
height: fit-content;
|
||||
font-size:14px;
|
||||
line-height: 14px;
|
||||
margin-left: var(--sl-nav-gap);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
a:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
@media (width < 450px) {
|
||||
.github-sign-in {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,20 +15,18 @@ export const isLoaded = derived(
|
||||
const TOKEN_KEY = 'TOKEN';
|
||||
|
||||
export function loadToken() {
|
||||
// Get the current value from localStorage if it exists, otherwise use the startValue
|
||||
const persistedValue = localStorage.getItem(TOKEN_KEY);
|
||||
if (persistedValue) {
|
||||
token.set(JSON.parse(persistedValue));
|
||||
return;
|
||||
const persistedToken = localStorage.getItem(TOKEN_KEY);
|
||||
if (persistedToken) {
|
||||
token.set(JSON.parse(persistedToken));
|
||||
}
|
||||
|
||||
token.set('API call');
|
||||
}
|
||||
|
||||
token.subscribe((value) => {
|
||||
if (value) {
|
||||
if (value === 'delete') {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(TOKEN_KEY, JSON.stringify(value));
|
||||
}
|
||||
});
|
||||
|
||||
export const test = writable('test');
|
||||
|
||||
45
docs/src/components/github/tooltip.js
Normal file
45
docs/src/components/github/tooltip.js
Normal file
@@ -0,0 +1,45 @@
|
||||
export function tooltip(element) {
|
||||
let div;
|
||||
let title;
|
||||
function mouseOver(event) {
|
||||
// NOTE: remove the `title` attribute, to prevent showing the default browser tooltip
|
||||
// remember to set it back on `mouseleave`
|
||||
title = element.getAttribute('title');
|
||||
element.removeAttribute('title');
|
||||
|
||||
div = document.createElement('div');
|
||||
div.textContent = title;
|
||||
div.style = `
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 1px 1px 1px #ddd;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
top: ${event.pageX + 5}px;
|
||||
left: ${event.pageY + 5}px;
|
||||
`;
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
function mouseMove(event) {
|
||||
div.style.left = `${event.pageX + 5}px`;
|
||||
div.style.top = `${event.pageY + 5}px`;
|
||||
}
|
||||
function mouseLeave() {
|
||||
document.body.removeChild(div);
|
||||
// NOTE: restore the `title` attribute
|
||||
element.setAttribute('title', title);
|
||||
}
|
||||
|
||||
element.addEventListener('mouseover', mouseOver);
|
||||
element.addEventListener('mouseleave', mouseLeave);
|
||||
element.addEventListener('mousemove', mouseMove);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
element.removeEventListener('mouseover', mouseOver);
|
||||
element.removeEventListener('mouseleave', mouseLeave);
|
||||
element.removeEventListener('mousemove', mouseMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
export const Icons = {
|
||||
heart:
|
||||
'<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>',
|
||||
star: '<path d="M22 9.67a1 1 0 0 0-.86-.67l-5.69-.83L12.9 3a1 1 0 0 0-1.8 0L8.55 8.16 2.86 9a1 1 0 0 0-.81.68 1 1 0 0 0 .25 1l4.13 4-1 5.68a1 1 0 0 0 1.45 1.07L12 18.76l5.1 2.68c.14.08.3.12.46.12a1 1 0 0 0 .99-1.19l-1-5.68 4.13-4A1 1 0 0 0 22 9.67Zm-6.15 4a1 1 0 0 0-.29.89l.72 4.19-3.76-2a1 1 0 0 0-.94 0l-3.76 2 .72-4.19a1 1 0 0 0-.29-.89l-3-3 4.21-.61a1 1 0 0 0 .76-.55L12 5.7l1.88 3.82a1 1 0 0 0 .76.55l4.21.61-3 2.99Z"/>',
|
||||
star: '<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"/>',
|
||||
fullStar:
|
||||
'<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"/>',
|
||||
fork: '<path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"/>',
|
||||
};
|
||||
|
||||
@@ -1,32 +1,8 @@
|
||||
import { defineMiddleware } from 'astro/middleware';
|
||||
|
||||
const GITHUB_OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
|
||||
import { defineMiddleware } from 'astro:middleware';
|
||||
|
||||
// `context` and `next` are automatically typed
|
||||
export const onRequest = defineMiddleware((context, next) => {
|
||||
console.log(context.url.pathname);
|
||||
if (context.url.pathname !== '/auth') {
|
||||
return next();
|
||||
}
|
||||
console.log(context.cookies);
|
||||
|
||||
const appReturnUrl = context.request.url;
|
||||
const url = new URL(context.request.url);
|
||||
|
||||
console.log('je rentre ici');
|
||||
console.log(context.url);
|
||||
console.log(context.site);
|
||||
// console.dir(context.params);
|
||||
|
||||
// if (!appReturnUrl) {
|
||||
// res.status(400).json({ error: '`redirect_uri` is required.' });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const { client_id } = env;
|
||||
// const redirect_uri = `http://${context.headers.host}/api/oauth/authorized`;
|
||||
// const state = await encodeState(appReturnUrl, env.encryption_password);
|
||||
//
|
||||
// const oauthParams = new URLSearchParams({ client_id, redirect_uri, state });
|
||||
// context.redirect(`${GITHUB_OAUTH_AUTHORIZE_URL}?${oauthParams}`, 302);
|
||||
|
||||
return Response.redirect(new URL('/', context.url));
|
||||
return next();
|
||||
});
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { toHexString } from '../../utils/encrypt';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const GITHUB_OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
|
||||
|
||||
export async function GET({params, redirect}) {
|
||||
export async function GET({url, redirect}) {
|
||||
|
||||
console.log('Authorize request', params);
|
||||
const myUrl = new URL(url);
|
||||
const params = new URLSearchParams(myUrl.search);
|
||||
const redirectUrl = params.get('redirect_uri');
|
||||
|
||||
const redirect_uri = 'http://localhost:4321/auth/localized'
|
||||
const { GITHUB_CLIENT_ID } = import.meta.env;
|
||||
|
||||
const oauthParams = new URLSearchParams({ client_id:'Iv1.711903007f608691' , redirect_uri, state: 'lqsksqd' });
|
||||
|
||||
const redirect_uri = 'http://localhost:4321/auth/authorized'
|
||||
const state = toHexString(redirectUrl);
|
||||
|
||||
const oauthParams = new URLSearchParams({ client_id:GITHUB_CLIENT_ID , redirect_uri, state });
|
||||
return redirect(`${GITHUB_OAUTH_AUTHORIZE_URL}?${oauthParams}`, 302)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,64 @@
|
||||
const GITHUB_OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
|
||||
import { fromHexString } from '../../utils/encrypt';
|
||||
|
||||
export async function GET({params, request}) {
|
||||
export const prerender = false;
|
||||
|
||||
console.log('Authorized', params);
|
||||
const GITHUB_OAUTH_ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
|
||||
const TOKEN_VALIDITY_PERIOD = 1000 * 60 * 60 * 24 * 365; // 1 year;
|
||||
|
||||
export async function GET({ url, redirect, cookies}) {
|
||||
|
||||
return new Response({
|
||||
status: 302,
|
||||
path: `/`,
|
||||
});
|
||||
const myUrl = new URL(url);
|
||||
const params = new URLSearchParams(myUrl.search);
|
||||
const code = params.get('code');
|
||||
const state = params.get('state');
|
||||
const error = params.get('error');
|
||||
|
||||
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = import.meta.env;
|
||||
|
||||
const redirectUrl = new URL(fromHexString(state));
|
||||
|
||||
console.log('Authorized', GITHUB_CLIENT_ID);
|
||||
|
||||
if (error && error === 'access_denied') {
|
||||
redirect(redirectUrl.href, 302);
|
||||
return;
|
||||
}
|
||||
|
||||
const init = {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ client_id: GITHUB_CLIENT_ID, client_secret: GITHUB_CLIENT_SECRET , code, state }),
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
};
|
||||
|
||||
let accessToken = '';
|
||||
let refreshToken = '';
|
||||
try {
|
||||
const response = await fetch(GITHUB_OAUTH_ACCESS_TOKEN_URL, init);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
accessToken = data.access_token;
|
||||
refreshToken = data.refresh_token;
|
||||
} else {
|
||||
throw new Error(`Access token response had status ${response.status}.`);
|
||||
}
|
||||
} catch (err) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: err.message
|
||||
}), {
|
||||
status: 503
|
||||
}
|
||||
)
|
||||
return;
|
||||
}
|
||||
|
||||
// cookies.set('token', accessToken, { expires: new Date(Date.now() + TOKEN_VALIDITY_PERIOD), secure: true, httpOnly: true, path: '/' });
|
||||
cookies.set('refresh', refreshToken, { secure: true, httpOnly: true, path: '/' });
|
||||
|
||||
redirectUrl.searchParams.set('token', accessToken);
|
||||
// redirectUrl.searchParams.set('refresh', refreshToken);
|
||||
|
||||
return redirect(redirectUrl.href, 302);
|
||||
}
|
||||
|
||||
62
docs/src/pages/auth/refresh.js
Normal file
62
docs/src/pages/auth/refresh.js
Normal file
@@ -0,0 +1,62 @@
|
||||
export const prerender = false;
|
||||
|
||||
const GITHUB_OAUTH_REFRESH_TOKEN = 'https://github.com/login/oauth/access_token';
|
||||
|
||||
export async function GET({cookies}) {
|
||||
|
||||
const refresh_token = cookies.get('refresh').value;
|
||||
|
||||
const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = import.meta.env;
|
||||
|
||||
|
||||
const init = {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams({ client_id: GITHUB_CLIENT_ID, client_secret: GITHUB_CLIENT_SECRET, grant_type: "refresh_token" , refresh_token }),
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
},
|
||||
};
|
||||
|
||||
let accessToken = '';
|
||||
let refreshToken = '';
|
||||
try {
|
||||
|
||||
const response = await fetch(GITHUB_OAUTH_REFRESH_TOKEN, init);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if(data.error) {
|
||||
cookies.delete('refresh');
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
token: 'delete'
|
||||
}), {
|
||||
status: 200
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
accessToken = data.access_token;
|
||||
refreshToken = data.refresh_token;
|
||||
} else {
|
||||
throw new Error(`Access token response had status ${response.status}.`);
|
||||
}
|
||||
} catch (err) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: err.message
|
||||
}), {
|
||||
status: 503
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
cookies.set('refresh', refreshToken, { secure: true, httpOnly: true, path: '/' });
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
token: accessToken
|
||||
}), {
|
||||
status: 200
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -58,11 +58,13 @@ html {
|
||||
}
|
||||
|
||||
a.action,
|
||||
.button-star,
|
||||
.article-footer > a,
|
||||
.action-footer > a.action-button {
|
||||
transition: var(--button-transition);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: var(--button-hover-opacity);
|
||||
transition: var(--button-transition);
|
||||
|
||||
@@ -189,3 +191,8 @@ details {
|
||||
.body:where(.astro-v5tidmuc) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.astro-kmkmnagf {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
15
docs/src/utils/encrypt.ts
Normal file
15
docs/src/utils/encrypt.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export function toHexString(value: string): string {
|
||||
return value
|
||||
.split('')
|
||||
.map((c) => c.charCodeAt(0).toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
}
|
||||
|
||||
export function fromHexString(hexString: string): string {
|
||||
return (
|
||||
hexString
|
||||
.match(/.{1,2}/g)
|
||||
?.map((byte) => String.fromCharCode(parseInt(byte, 16)))
|
||||
.join('') ?? ''
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user