1
Fork 0
This commit is contained in:
Korbs 2024-07-24 15:53:15 -04:00
parent 6f3a08867f
commit 36d554c16b
30 changed files with 5790 additions and 650 deletions

View file

@ -19,9 +19,6 @@ if (Astro.cookies.get("Telemtry") === undefined) {
Astro.cookies.set("Telemtry", "Disabled", {path: "/",sameSite: 'strict'})
}
//// Check what language the user has set it to and switch to it
// Properties
const {
Title,
@ -99,5 +96,4 @@ if (Astro.url.href.match('watch')) {
</head>
<Analytics/>
<script type="module" is:inline>
</script>
<script is:inline src="./assets/vendor/preline/preline.js"></script>

5
src/env.d.ts vendored
View file

@ -1 +1,6 @@
/// <reference types="astro/client" />
declare namespace App {
interface Locals {
email: string;
}
}

View file

@ -21,4 +21,4 @@ import '@styles/mobile.scss'
<Header/>
<slot/>
</div>
<Footer/>
<Footer/>

115
src/layouts/Settings.astro Normal file
View file

@ -0,0 +1,115 @@
---
// Properties
const { Title, Description } = Astro.props
// Components
import Head from '@components/global/Head.astro'
import MobileNav from '@components/global/MobileNav.astro'
import Sidebar from '@components/global/Sidebar.astro'
import Footer from '@components/global/Footer.astro'
// Styles
import '@styles/index.scss'
import '@styles/mobile.scss'
import { CodeBrackets, EditPencil, EvPlugXmark, FloppyDisk, HelpCircle, InfoCircle, LogOut, Play, PrivacyPolicy, ProfileCircle, Server, UnionAlt, XmarkCircle } from '@iconoir/vue'
---
<Head Title={Title} Description={Description}/>
<MobileNav/>
<Sidebar/>
<div class="content settings-area">
<div class="settings-sidebar">
<div>
<h2>General</h2>
<a id="sidebar-disable" href="#"><EditPencil/> Customization</a>
<a id="sidebar-disable" href="#"><Play/> Video Player</a>
<a id="sidebar-disable" href="#"><CodeBrackets/> CSS/JS</a>
<h2>You</h2>
<a href="#"><ProfileCircle/> Account</a>
<h2>Advanced</h2>
<a id="sidebar-disable" href="#"><EvPlugXmark/> Reset</a>
<a id="sidebar-disable" href="#"><FloppyDisk/> Backup & Restore</a>
<a id="sidebar-disable" href="#"><XmarkCircle/> Delete Account</a>
</div>
<div>
<a id="sidebar-disable" href="#"><HelpCircle/> Help</a>
<a href="#"><UnionAlt/> Telemtry</a>
<a href="#"><Server/> Instance</a>
<a href="#"><PrivacyPolicy/> Privacy Policy</a>
<hr/>
<a href="#"><LogOut/> Log Out</a>
</div>
</div>
<div class="settings-content">
<slot/>
</div>
</div>
<Footer/>
<style is:inline>a[href="/settings"] {background: rgb(255 255 255 / 25%) !important;border: 2px rgba(255, 255, 255, 0.25) solid !important;}</style>
<style lang="scss">
.settings-area {
display: flex;
gap: 24px;
position: relative;
flex-wrap: wrap;
align-items: stretch;
min-height: 100vh;
.settings-content {
max-width: 750px;
margin: 0px auto;
}
.settings-sidebar {
background: #161616;
width: 180px;
color: white;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 0px 12px;
div {
position: sticky;
}
div:nth-child(1) {
position: sticky;
top: 0px;
}
div:nth-child(2) {
position: sticky;
bottom: 0px;
}
h2 {
font-size: 16px;
padding-left: 12px;
}
a {
color: white;
text-decoration: none;
margin-bottom: 6px;
border-radius: 6px;
padding: 4px 12px;
background: transparent;
border: 2px transparent solid;
display: flex;
align-items: center;
gap: 14px;
font-size: 14px;
&:hover {
background: rgb(255 255 255 / 05%);
border: 2px rgba(255, 255, 255, 0.05) solid;
}
svg {
width: 18px;
}
&#sidebar-disable {
filter: brightness(0.4);
cursor: not-allowed;
&:hover {
background: transparent;
border: 2px transparent solid;
}
}
}
}
}
</style>

14
src/library/supabase.ts Normal file
View file

@ -0,0 +1,14 @@
import { createClient } from "@supabase/supabase-js";
export const supabase = createClient(
import.meta.env.SUPABASE_URL,
import.meta.env.SUPABASE_ANON_KEY,
{
auth: {
flowType: "pkce",
autoRefreshToken: false,
detectSessionInUrl: false,
persistSession: true,
},
},
);

View file

@ -0,0 +1,53 @@
---
import i18next,{ t, changeLanguage } from "i18next";
import Base from "@layouts/Default.astro";
import '@styles/form.scss'
---
<Base Title="MinPluto" Description="">
<div class="force-center">
<img src="/images/logo/MinPluto - Image Logo Full with Shadow.png"/>
<hr/>
<img src="/images/backgrounds/1.webp"/>
</div>
<form action="/api/auth/confirm" method="post">
<div class="form-field">
<label>Email</label>
<input name="email" type="email" autocomplete="off" required/>
</div>
<div class="form-field">
<label>Code</label>
<input name="code" type="text" autocomplete="off" required/>
</div>
<div class="form-actions">
<div></div>
<div>
<span></span>
<button type="submit">Confirm</button>
</div>
</div>
</form>
</Base>
<style is:global>
img[src="/images/backgrounds/1.webp"] {
position: fixed;
bottom: 0px;
left: 50%;
transform: translate(-57%);
-webkit-mask-image: linear-gradient(180deg, transparent 5%, rgb(0 0 0) 52%, rgb(0 0 0) 44%, transparent 95%);
}
@media only screen and (min-width: 875px) {
img[src="/images/backgrounds/1.webp"] {
display: none;
}
}
.force-center {
text-align: center;
padding-top: 10%;
}
.search-bar {
display: none !important;
}
</style>

View file

@ -0,0 +1,59 @@
---
// i18n
import i18next,{ t, changeLanguage } from "i18next";
import Settings from "@layouts/Settings.astro";
// Supabase Data
import { supabase } from "@library/supabase"
const { data: { user } } = await supabase.auth.getUser()
const ID = user?.id
const Name = user?.user_metadata.name
const Email = user?.email
// Styles
import '@styles/form.scss'
---
<Settings Title="Account" Description="Manage your account">
<p>Account ID: {ID}</p>
<form action="/api/update/name" method="post">
<div class="form-field">
<label>Username</label>
<input name="name" type="text" autocomplete="off" value={Name} required/>
</div>
<div class="form-actions"><div></div><div><span></span><button type="submit">Update</button></div></div>
</form>
<form>
<div class="form-field">
<label>Email</label>
<input name="email" type="email" autocomplete="off" value={Email} readonly/>
</div>
<div class="form-actions"><div></div><div><span></span><button type="submit">Update</button></div></div>
</form>
<form onkeypress="CheckPasswordConfirmation()">
<div class="form-field">
<label>Password</label>
<input id="password" name="password" type="password" autocomplete="off" required/>
</div>
<div class="form-field">
<label>Confirm Password</label>
<input id="confirm_password" name="password" type="password" autocomplete="off" required/>
</div>
<div class="form-actions">
<div>
<a href="#">Forgot Password</a>
</div>
<div><span></span><button type="submit">Update</button></div>
</div>
</form>
</Settings>
<script is:inline>
function CheckPasswordConfirmation() {
if (document.getElementById('confirm_password').value !== document.getElementById('password').value) {
document.getElementById('confirm_password').setCustomValidity('Password Must be Matching.');
} else {
document.getElementById('confirm_password').setCustomValidity('');
}
}
</script>

View file

@ -0,0 +1,20 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData()
const EMAIL = formData.get("email")?.toString()
const OTP = formData.get("code")?.toString()
if (!OTP) {
return new Response("Code required", { status: 400 })
}
const { error } = await supabase.auth.verifyOtp({type: 'email', token: OTP, email: EMAIL})
if (error) {
return new Response(error.message, { status: 500 })
}
return redirect("/login?=confirmed")
}

View file

@ -0,0 +1,35 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
export const POST: APIRoute = async ({ request, cookies, redirect }) => {
const formData = await request.formData()
const email = formData.get("email")?.toString()
const password = formData.get("password")?.toString()
if (!email || !password) {
return new Response("Email and password are required", { status: 400 })
}
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
return new Response(error.message, { status: 500 })
}
const { access_token, refresh_token } = data.session
cookies.set("sb-access-token", access_token, {
sameSite: "strict",
path: "/",
secure: true,
})
cookies.set("sb-refresh-token", refresh_token, {
sameSite: "strict",
path: "/",
secure: true,
})
return redirect("/")
}

View file

@ -0,0 +1,15 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
import type { Provider } from "@supabase/supabase-js"
export const GET: APIRoute = async ({ cookies, redirect }) => {
if(cookies.get('anonymous-session')) {
return redirect('/account/anon/end')
} else {
cookies.delete("sb-access-token", { path: "/" })
cookies.delete("sb-refresh-token", { path: "/" })
const { error } = await supabase.auth.signOut()
}
return redirect("/")
}

View file

@ -0,0 +1,40 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData()
const name = formData.get("username")?.toString()
const email = formData.get("email")?.toString()
const password = formData.get("password")?.toString()
if (!email || !password || !name) {
return new Response("Email and password are required", { status: 400 })
}
const { error } = await supabase.auth.signUp({
options: {
emailRedirectTo: "http://localhost:1930/?=welcome",
data: {
name: name,
ui_theme: "Default",
ui_color: "Default",
ui_zen: "false",
ui_sidebar_size: "Normal",
invidous_data: "https://yt.sudovanilla.org",
invidous_media: "https://yt.sudovanilla.org",
safetwitch_data: "https://twitch.sudovanilla.org",
safetwitch_media: "https://twitch.sudovanilla.org",
image_proxy: "https://ipx.sudovanilla.org",
player_type: "Zorn"
}
},
email,
password,
})
if (error) {
return new Response(error.message, { status: 500 })
}
return redirect("/account/confirm-your-email")
}

View file

@ -0,0 +1,22 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData()
const NewEmail = formData.get("email")?.toString()
if (!NewEmail) {
return new Response("Error.", { status: 400 })
}
const { error } = await supabase.auth.resend({
type: 'email_change',
email: NewEmail
})
if (error) {
return new Response(error.message, { status: 500 })
}
return redirect("/account?=CheckEmail")
}

View file

@ -0,0 +1,21 @@
import type { APIRoute } from "astro"
import { supabase } from "@library/supabase"
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData()
const name = formData.get("name")?.toString()
if (!name) {
return new Response("Error.", { status: 400 })
}
const { error } = await supabase.auth.updateUser({
data: {name: name}
})
if (error) {
return new Response(error.message, { status: 500 })
}
return redirect("/account?=NameUpdated")
}

View file

@ -3,7 +3,7 @@ import { t, changeLanguage } from "i18next";
import Embed from "@layouts/Embed.astro";
import "@styles/video.scss";
// Configuration
import { DEFAULT_MEDIA_DATA_PROXY, DEFAULT_MEDIA_DATA_PROXY, DEFAULT_IMAGE_PROXY, SERVER_DOMAIN } from '@utils/GetConfig'
import { DEFAULT_MEDIA_DATA_PROXY, DEFAULT_IMAGE_PROXY, SERVER_DOMAIN } from '@utils/GetConfig'
// Fetch
const SWV = Astro.url.href.split("embed/").pop();

View file

@ -78,6 +78,8 @@ const GamingSplit = GamingData.slice(0, 1)
</div>
</Base>
<style is:inline>.sidebar-top-end a[href="/"] {background: rgb(255 255 255 / 25%) !important;border: 2px rgba(255, 255, 255, 0.25) solid !important;}</style>
<style>
img[src="/images/backgrounds/1.webp"] {
position: fixed;

View file

@ -1,5 +1,5 @@
---
layout: '@layouts/Markdown.astro'
layout: '@layouts/Settings.astro'
---
import {

58
src/pages/login.astro Normal file
View file

@ -0,0 +1,58 @@
---
import i18next,{ t, changeLanguage } from "i18next";
import Base from "@layouts/Default.astro";
import '@styles/form.scss'
---
<Base Title="MinPluto" Description="">
<div class="force-center">
<img src="/images/logo/MinPluto - Image Logo Full with Shadow.png"/>
<hr/>
<img src="/images/backgrounds/1.webp"/>
</div>
<form action="/api/auth/login" method="post">
<div class="form-field">
<label>Email</label>
<input name="email" type="email" autocomplete="off" required/>
</div>
<div class="form-field">
<label>Password</label>
<input name="password" type="password" autocomplete="off" required/>
</div>
<div class="form-actions">
<div>
<a aria-disabled="true" href="#">Forgot Password</a>
</div>
<div>
<button onclick="location.href = '/register/'">Create Account</button>
<button type="submit">Login</button>
</div>
</div>
</form>
</Base>
<style is:inline>a[href="/login"] {background: rgb(255 255 255 / 25%) !important;border: 2px rgba(255, 255, 255, 0.25) solid !important;}</style>
<style is:global>
img[src="/images/backgrounds/1.webp"] {
position: fixed;
bottom: 0px;
left: 50%;
transform: translate(-57%);
-webkit-mask-image: linear-gradient(180deg, transparent 5%, rgb(0 0 0) 52%, rgb(0 0 0) 44%, transparent 95%);
}
@media only screen and (min-width: 875px) {
img[src="/images/backgrounds/1.webp"] {
display: none;
}
}
.force-center {
text-align: center;
padding-top: 10%;
}
.search-bar {
display: none !important;
}
form {
max-width: 500px !important;
}
</style>

View file

@ -1,5 +1,5 @@
---
layout: "@layouts/Markdown.astro"
layout: "@layouts/Settings.astro"
---
import {

66
src/pages/register.astro Normal file
View file

@ -0,0 +1,66 @@
---
import i18next,{ t, changeLanguage } from "i18next";
import Base from "@layouts/Default.astro";
import '@styles/form.scss'
---
<Base Title="MinPluto" Description="">
<div class="force-center">
<img src="/images/logo/MinPluto - Image Logo Full with Shadow.png"/>
<hr/>
<img src="/images/backgrounds/1.webp"/>
</div>
<form action="/api/auth/register" method="post">
<div class="form-field">
<label>Username</label>
<input name="username" type="text" autocomplete="off" required/>
</div>
<div class="form-field">
<label>Email</label>
<input name="email" type="email" autocomplete="off" required/>
</div>
<div style="display: flex; justify-content: space-between; gap: 6px;">
<div style="width: 100%;" class="form-field">
<label>Password</label>
<input name="password" type="password" autocomplete="off" required/>
</div>
</div>
<div class="form-actions">
<div>
<a href="/privacy">Privacy Policy</a>
</div>
<div>
<button onclick="location.href = '/login'">Login</button>
<button type="submit">Register</button>
</div>
</div>
</form>
</Base>
<style is:global>
img[src="/images/backgrounds/1.webp"] {
position: fixed;
bottom: 0px;
left: 50%;
transform: translate(-57%);
-webkit-mask-image: linear-gradient(180deg, transparent 5%, rgb(0 0 0) 52%, rgb(0 0 0) 44%, transparent 95%);
z-index: -1;
opacity: 0.5;
}
@media only screen and (min-width: 875px) {
img[src="/images/backgrounds/1.webp"] {
display: none;
}
}
.force-center {
text-align: center;
padding-top: 10%;
}
.search-bar {
display: none !important;
}
form {
max-width: 500px !important;
}
</style>

View file

@ -28,12 +28,37 @@
"STATUS": "Status",
"FORUM": "Forum",
"SOURCE_CODE": "Source Code"
},
"ACCOUNT": {
"CUSTOMIZATION": "",
"VIDEO_PLAYER": "",
"CSS/JS": "",
"ACCOUNT": "",
"RESET": "",
"BACKUP_RESTORE": "",
"DELETE_ACCOUNT": "",
"HEADERS": {
"SETTINGS": "Settings",
"YOU": "You",
"ADVANCE": "Advance"
}
}
},
"HEADER": {
"SEARCH": "Search",
"FEEDBACK": "Feedback"
},
"FORM": {
"EMAIL": "Email",
"PASSWORD": "Password",
"USERNAME": "Username",
"CREATE_ACCOUNT": "Create Account",
"FORGOT_PASSWORD": "Forgot Password",
"LOGIN": "Login",
"REGISTER": "Register",
"UPDATE": "Update",
"SUBMIT": "Submit"
},
"TELEMTRY": {
"OPTIN": "Opt-In",
"OPTOUT": "Opt-Out"

59
src/styles/form.scss Normal file
View file

@ -0,0 +1,59 @@
form {
margin: auto;
display: grid;
gap: 12px;
margin-bottom: 12px;
.form-field {
display: flex;
flex-direction: column;
gap: 6px;
background: #161616;
color: white;
border: 1px #2d2d2d solid;
border-radius: 4px;
padding: 6px 12px;
position: relative;
label {
font-size: 12px;
color: rgb(145, 145, 145);
z-index: 1;
pointer-events: none;
}
input {
background: transparent;
border: none;
color: white;
padding: 32px 12px 12px 12px;
margin: -26px -12px -6px -12px;
border-radius: 4px;
&:hover {
background: rgb(32, 32, 32);
}
&:focus {
background: rgb(48, 48, 48);
outline: none;
}
}
}
.form-actions {
display: flex;
align-items: center;
justify-content: space-between;
button {
background: white;
color: black;
padding: 6px 12px;
border-radius: 3rem;
border: 1px white solid;
cursor: pointer;
&:hover {
filter: brightness(0.8);
}
&:nth-child(1) {
border: 1px white solid;
background: transparent;
color: white;
}
}
}
}

View file

@ -6,7 +6,7 @@ body {
top: 0px;
right: 0px;
margin: auto;
width: calc(100% - 248px);
width: calc(100% - 204px);
}
a {