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 starlight from '@astrojs/starlight';
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
import svelte from '@astrojs/svelte';
|
import svelte from '@astrojs/svelte';
|
||||||
|
import vercel from '@astrojs/vercel/serverless';
|
||||||
|
|
||||||
export const locales = {
|
export const locales = {
|
||||||
root: {
|
root: {
|
||||||
@@ -66,7 +67,7 @@ export default defineConfig({
|
|||||||
// ru: 'Leaderboard'
|
// ru: 'Leaderboard'
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
label: 'Challenges',
|
label: 'Challenges',
|
||||||
autogenerate: {
|
autogenerate: {
|
||||||
directory: 'challenges'
|
directory: 'challenges'
|
||||||
@@ -109,5 +110,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
defaultLocale: 'root',
|
defaultLocale: 'root',
|
||||||
locales
|
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": {
|
"dependencies": {
|
||||||
"@astrojs/starlight": "^0.15.1",
|
"@astrojs/starlight": "^0.15.1",
|
||||||
"@astrojs/svelte": "^5.2.0",
|
"@astrojs/svelte": "^5.2.0",
|
||||||
|
"@astrojs/vercel": "^7.5.0",
|
||||||
"@fontsource/ibm-plex-serif": "^5.0.8",
|
"@fontsource/ibm-plex-serif": "^5.0.8",
|
||||||
"astro": "^4.0.0",
|
"astro": "^4.0.0",
|
||||||
"sharp": "^0.32.5",
|
"sharp": "^0.32.5",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { getEntry } from 'astro:content';
|
|||||||
|
|
||||||
const { lang } = Astro.props;
|
const { lang } = Astro.props;
|
||||||
const { data } = await getEntry('i18n', lang);
|
const { data } = await getEntry('i18n', lang);
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="action-footer">
|
<div class="action-footer">
|
||||||
@@ -37,7 +38,6 @@ const { data } = await getEntry('i18n', lang);
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: var(--sl-text-sm) !important;
|
font-size: var(--sl-text-sm) !important;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
font-size: var(--sl-text-base);
|
|
||||||
padding: 0.4rem 0.8rem;
|
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 Default from '@astrojs/starlight/components/SiteTitle.astro';
|
||||||
import GitHubStats from './GitHubStats.svelte';
|
|
||||||
import MyIcon from './MyIcon.astro';
|
import MyIcon from './MyIcon.astro';
|
||||||
import SignUp from './github/SignUp.svelte';
|
import SignUp from './github/SignUp.svelte';
|
||||||
|
import { Icon } from '@astrojs/starlight/components';
|
||||||
---
|
---
|
||||||
|
|
||||||
<Default {...Astro.props}>
|
<Default {...Astro.props}>
|
||||||
<slot />
|
<slot />
|
||||||
</Default>
|
</Default>
|
||||||
|
|
||||||
<SignUp client:load />
|
|
||||||
|
|
||||||
<!--<GitHubStats client:load >-->
|
<SignUp client:only="svelte" >
|
||||||
<!-- <MyIcon name="star" slot="star" />-->
|
<Icon name="github" slot="github" />
|
||||||
<!-- <MyIcon name="fork" viewBox="0 0 16 16" slot="fork"/>-->
|
<MyIcon name="fullStar" slot="fullStar" viewBox="0 0 16 16" fill="#e3b341"/>
|
||||||
<!--</GitHubStats>-->
|
<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>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { data, error, isLoaded, isLoading, token, totalCount } from './github-store';
|
import { data, error, isLoaded, isLoading, token, totalCount } from './github-store';
|
||||||
|
|
||||||
export let challengeNumber;
|
export let challengeNumber;
|
||||||
|
|
||||||
let page = 1;
|
let page = 1;
|
||||||
|
|
||||||
|
token.subscribe(token => {
|
||||||
|
if (token) {
|
||||||
|
fetchTotalCount();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchTotalCount() {
|
async function fetchTotalCount() {
|
||||||
isLoading.set(true);
|
isLoading.set(true);
|
||||||
try {
|
try {
|
||||||
while (true) {
|
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) {
|
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();
|
const { items: new_items, total_count } = await response.json();
|
||||||
if (!new_items || new_items.length === 0) break;
|
if (!new_items || new_items.length === 0) break;
|
||||||
@@ -30,13 +39,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
fetchTotalCount();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
token: {$token}
|
|
||||||
{#if $isLoaded}
|
{#if $isLoaded}
|
||||||
<div class="solution-container" id="answers">
|
<div class="solution-container" id="answers">
|
||||||
<div>Answered by</div>
|
<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>
|
<script>
|
||||||
import { loadToken, test } from './github-store';
|
import GitHubStats from './GitHubStats.svelte';
|
||||||
|
import { loadToken, token } from './github-store';
|
||||||
import { onMount } from 'svelte';
|
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(() => {
|
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>
|
</script>
|
||||||
|
|
||||||
{$test}
|
{#if !$token}
|
||||||
<button on:click={redirectToGitHubSignup}>
|
<a href={`/auth/authorize?redirect_uri=${window.location.href}`}>
|
||||||
Sign Up for GitHub
|
<slot name="github"/>
|
||||||
</button>
|
<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>
|
<style>
|
||||||
button {
|
a {
|
||||||
background-color: #2ea44f;
|
background-color: #238636;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
text-decoration: none;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 10px 20px;
|
padding: 8px 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 5px;
|
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;
|
background-color: #218838;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (width < 450px) {
|
||||||
|
.github-sign-in {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,20 +15,18 @@ export const isLoaded = derived(
|
|||||||
const TOKEN_KEY = 'TOKEN';
|
const TOKEN_KEY = 'TOKEN';
|
||||||
|
|
||||||
export function loadToken() {
|
export function loadToken() {
|
||||||
// Get the current value from localStorage if it exists, otherwise use the startValue
|
const persistedToken = localStorage.getItem(TOKEN_KEY);
|
||||||
const persistedValue = localStorage.getItem(TOKEN_KEY);
|
if (persistedToken) {
|
||||||
if (persistedValue) {
|
token.set(JSON.parse(persistedToken));
|
||||||
token.set(JSON.parse(persistedValue));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token.set('API call');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token.subscribe((value) => {
|
token.subscribe((value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
if (value === 'delete') {
|
||||||
|
localStorage.removeItem(TOKEN_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
localStorage.setItem(TOKEN_KEY, JSON.stringify(value));
|
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 = {
|
export const Icons = {
|
||||||
heart:
|
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>',
|
'<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"/>',
|
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';
|
import { defineMiddleware } from 'astro:middleware';
|
||||||
|
|
||||||
const GITHUB_OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
|
|
||||||
|
|
||||||
|
// `context` and `next` are automatically typed
|
||||||
export const onRequest = defineMiddleware((context, next) => {
|
export const onRequest = defineMiddleware((context, next) => {
|
||||||
console.log(context.url.pathname);
|
console.log(context.cookies);
|
||||||
if (context.url.pathname !== '/auth') {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
const appReturnUrl = context.request.url;
|
return next();
|
||||||
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));
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
|
import { toHexString } from '../../utils/encrypt';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
const GITHUB_OAUTH_AUTHORIZE_URL = 'https://github.com/login/oauth/authorize';
|
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)
|
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({
|
const myUrl = new URL(url);
|
||||||
status: 302,
|
const params = new URLSearchParams(myUrl.search);
|
||||||
path: `/`,
|
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,
|
a.action,
|
||||||
|
.button-star,
|
||||||
.article-footer > a,
|
.article-footer > a,
|
||||||
.action-footer > a.action-button {
|
.action-footer > a.action-button {
|
||||||
transition: var(--button-transition);
|
transition: var(--button-transition);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
opacity: var(--button-hover-opacity);
|
opacity: var(--button-hover-opacity);
|
||||||
transition: var(--button-transition);
|
transition: var(--button-transition);
|
||||||
|
|
||||||
@@ -189,3 +191,8 @@ details {
|
|||||||
.body:where(.astro-v5tidmuc) {
|
.body:where(.astro-v5tidmuc) {
|
||||||
height: 100%;
|
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