Skip to content

Commit

Permalink
feat: site password protection
Browse files Browse the repository at this point in the history
  • Loading branch information
ddiu8081 committed Mar 8, 2023
1 parent c67419f commit 3cc6137
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 34 deletions.
2 changes: 2 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import unocss from 'unocss/astro'
import { presetUno } from 'unocss'
import presetAttributify from '@unocss/preset-attributify'
import presetTypography from '@unocss/preset-typography'
import presetIcons from '@unocss/preset-icons'
import solidJs from '@astrojs/solid-js'
import vercelDisableBlocks from './plugins/vercelDisableBlocks'

Expand Down Expand Up @@ -33,6 +34,7 @@ export default defineConfig({
}
}
}),
presetIcons(),
]
}),
solidJs()
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
"undici": "^5.20.0"
},
"devDependencies": {
"@iconify-json/carbon": "^1.1.16",
"@types/markdown-it": "^12.2.3",
"@unocss/preset-attributify": "^0.50.1",
"@unocss/preset-icons": "^0.50.4",
"@unocss/preset-typography": "^0.50.3",
"punycode": "^2.3.0",
"unocss": "^0.50.1"
Expand Down
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/components/Generator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default () => {
const requestWithLatestMessage = async () => {
setLoading(true)
setCurrentAssistantMessage('')
const storagePassword = localStorage.getItem('pass')
try {
const controller = new AbortController()
setController(controller)
Expand All @@ -57,6 +58,7 @@ export default () => {
body: JSON.stringify({
messages: requestMessageList,
time: timestamp,
pass: storagePassword,
sign: await generateSignature({
t: timestamp,
m: requestMessageList?.[requestMessageList.length - 1]?.content || '',
Expand Down
13 changes: 0 additions & 13 deletions src/components/Themetoggle.astro
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,13 @@
</style>

<script>
const initTheme = () => {
const darkSchema = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
const storageTheme = localStorage.getItem('theme')

if (storageTheme) {
document.documentElement.classList.toggle('dark', storageTheme === 'dark')
} else {
document.documentElement.classList.toggle('dark', darkSchema)
}
}

const listenColorSchema = () => {
const colorSchema = window.matchMedia('(prefers-color-scheme: dark)')
colorSchema.addEventListener('change', () => {
document.documentElement.classList.toggle('dark', colorSchema.matches)
})
}

initTheme()

listenColorSchema()

const handleToggleClick = () => {
Expand Down
14 changes: 12 additions & 2 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ const { title } = Astro.props;
</style>

<script>
const darkSchema = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
document.documentElement.classList.toggle('dark', darkSchema)
const initTheme = () => {
const darkSchema = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
const storageTheme = localStorage.getItem('theme')
if (storageTheme) {
document.documentElement.classList.toggle('dark', storageTheme === 'dark')
} else {
document.documentElement.classList.toggle('dark', darkSchema)
}
}

initTheme()

</script>
3 changes: 1 addition & 2 deletions src/pages/api/password_auth.ts → src/pages/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export const post: APIRoute = async (context) => {

const { pass } = body
return new Response(JSON.stringify({
real: realPassword,
input: pass,
code: (!realPassword || pass === realPassword) ? 0 : -1,
}))
}
6 changes: 5 additions & 1 deletion src/pages/api/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import { fetch, ProxyAgent } from 'undici'
const apiKey = import.meta.env.OPENAI_API_KEY
const httpsProxy = import.meta.env.HTTPS_PROXY
const baseUrl = (import.meta.env.OPENAI_API_BASE_URL || 'https://api.openai.com').trim().replace(/\/$/,'')
const sitePassword = import.meta.env.SITE_PASSWORD

export const post: APIRoute = async (context) => {
const body = await context.request.json()
const { sign, time, messages } = body
const { sign, time, messages, pass } = body
if (!messages) {
return new Response('No input text')
}
if (sitePassword && sitePassword !== pass) {
return new Response('Invalid password')
}
if (import.meta.env.PROD && !await verifySignature({ t: time, m: messages?.[messages.length - 1]?.content || '', }, sign)) {
return new Response('Invalid signature')
}
Expand Down
20 changes: 20 additions & 0 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,23 @@ import 'highlight.js/styles/atom-one-dark.css'
</main>
</Layout>

<script>
async function checkCurrentAuth() {
const password = localStorage.getItem('pass')
const response = await fetch('/api/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pass: password,
}),
})
const responseJson = await response.json()
if (responseJson.code !== 0) {
window.location.href = '/password'
}
}
checkCurrentAuth()
</script>

71 changes: 55 additions & 16 deletions src/pages/password.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,71 @@
import Layout from '../layouts/Layout.astro'
---

<Layout title="ChatGPT API Demo">
<Layout title="Password Protection">
<main class="h-screen flex flex-col items-center justify-center">
<div class="op-40">Please input password</div>
<input id="password_input" type="password" class="mt-2 px-4 py-3 rounded-sm bg-slate bg-op-15 focus:bg-op-20 focus:ring-0 focus:outline-none" />
<div class="op-30">Please input password</div>
<div id="input_container" class="flex mt-4">
<input id="password_input" type="password" class="px-4 py-3 h-12 rounded-sm bg-slate bg-op-15 focus:bg-op-20 focus:ring-0 focus:outline-none" />
<div id="submit" class="flex items-center justify-center h-12 w-12 bg-slate cursor-pointer bg-op-20 hover:bg-op-50">
<div class="i-carbon-arrow-right" />
</div>
</div>
</main>
</Layout>

<script>

const inputContainer = document.getElementById('input_container') as HTMLDivElement
const input = document.getElementById('password_input') as HTMLInputElement
const submitButton = document.getElementById('submit') as HTMLDivElement

input.onkeydown = async (event) => {
if (event.key === 'Enter') {
const password = input.value
const response = await fetch('/api/password_auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pass: password,
}),
})
const responseJson = await response.json()
console.log(responseJson)
handleSubmit()
}
}
submitButton.onclick = handleSubmit

async function handleSubmit() {
const password = input.value
const response = await fetch('/api/auth', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
pass: password,
}),
})
const responseJson = await response.json()
if (responseJson.code === 0) {
localStorage.setItem('pass', password)
window.location.href = '/'
} else {
inputContainer.classList.add('invalid')
setTimeout(() => {
inputContainer.classList.remove('invalid')
}, 300)
}
}
</script>

<style>
@keyframes shake {
0% {
transform: translateX(0);
}
25% {
transform: translateX(0.5rem);
}
75% {
transform: translateX(-0.5rem);
}
100% {
transform: translateX(0);
}
}

.invalid {
animation: shake 0.2s ease-in-out 0s 2;
}
</style>

0 comments on commit 3cc6137

Please sign in to comment.