jank-client-fork/webpage/login.ts
2024-09-06 15:43:11 -05:00

454 lines
13 KiB
TypeScript

import{ Dialog }from"./dialog.js";
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
function setTheme(){
let name=localStorage.getItem("theme");
if(!name){
localStorage.setItem("theme","Dark");
name="Dark";
}
document.body.className=name+"-theme";
}
let instances:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean,
uptime:{alltime:number,daytime:number,weektime:number},
urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[]|null;
setTheme();
function getBulkUsers(){
const json=getBulkInfo();
for(const thing in json.users){
json.users[thing]=new Specialuser(json.users[thing]);
}
return json;
}
function trimswitcher(){
const json=getBulkInfo();
const map=new Map();
for(const thing in json.users){
const user=json.users[thing];
let wellknown=user.serverurls.wellknown;
if(wellknown.at(-1)!=="/"){
wellknown+="/";
}
wellknown+=user.username;
if(map.has(wellknown)){
const otheruser=map.get(wellknown);
if(otheruser[1].serverurls.wellknown.at(-1)==="/"){
delete json.users[otheruser[0]];
map.set(wellknown,[thing,user]);
}else{
delete json.users[thing];
}
}else{
map.set(wellknown,[thing,user]);
}
}
for(const thing in json.users){
if(thing.at(-1)==="/"){
const user=json.users[thing];
delete json.users[thing];
json.users[thing.slice(0, -1)]=user;
}
}
localStorage.setItem("userinfos",JSON.stringify(json));
console.log(json);
}
function getBulkInfo(){
return JSON.parse(localStorage.getItem("userinfos"));
}
function setDefaults(){
let userinfos=getBulkInfo();
if(!userinfos){
localStorage.setItem("userinfos",JSON.stringify({
currentuser: null,
users: {},
preferences:
{
theme: "Dark",
notifications: false,
notisound: "three",
},
}));
userinfos=getBulkInfo();
}
if(userinfos.users===undefined){
userinfos.users={};
}
if(userinfos.accent_color===undefined){
userinfos.accent_color="#242443";
}
document.documentElement.style.setProperty("--accent-color", userinfos.accent_color);
if(userinfos.preferences===undefined){
userinfos.preferences={
theme: "Dark",
notifications: false,
notisound: "three",
};
}
if(userinfos.preferences&&(userinfos.preferences.notisound===undefined)){
userinfos.preferences.notisound="three";
}
localStorage.setItem("userinfos",JSON.stringify(userinfos));
}
setDefaults();
class Specialuser{
serverurls:{api:string,cdn:string,gateway:string,wellknown:string,login:string};
email:string;
token:string;
loggedin;
json;
constructor(json){
if(json instanceof Specialuser){
console.error("specialuser can't construct from another specialuser");
}
this.serverurls=json.serverurls;
let apistring=new URL(json.serverurls.api).toString();
apistring=apistring.replace(/\/(v\d+\/?)?$/, "")+"/v9";
this.serverurls.api=apistring;
this.serverurls.cdn=new URL(json.serverurls.cdn).toString().replace(/\/$/,"");
this.serverurls.gateway=new URL(json.serverurls.gateway).toString().replace(/\/$/,"");
this.serverurls.wellknown=new URL(json.serverurls.wellknown).toString().replace(/\/$/,"");
this.serverurls.login=new URL(json.serverurls.login).toString().replace(/\/$/,"");
this.email=json.email;
this.token=json.token;
this.loggedin=json.loggedin;
this.json=json;
this.json.localuserStore??={};
if(!this.serverurls||!this.email||!this.token){
console.error("There are fundamentally missing pieces of info missing from this user");
}
}
set pfpsrc(e){
this.json.pfpsrc=e;
this.updateLocal();
}
get pfpsrc(){
return this.json.pfpsrc;
}
set username(e){
this.json.username=e;
this.updateLocal();
}
get username(){
return this.json.username;
}
set localuserStore(e){
this.json.localuserStore=e;
this.updateLocal();
}
get localuserStore(){
return this.json.localuserStore;
}
get uid(){
return this.email+this.serverurls.wellknown;
}
toJSON(){
return this.json;
}
updateLocal(){
const info=getBulkInfo();
info.users[this.uid]=this.toJSON();
localStorage.setItem("userinfos",JSON.stringify(info));
}
}
function adduser(user){
user=new Specialuser(user);
const info=getBulkInfo();
info.users[user.uid]=user;
info.currentuser=user.uid;
localStorage.setItem("userinfos",JSON.stringify(info));
return user;
}
const instancein=document.getElementById("instancein") as HTMLInputElement;
let timeout;
let instanceinfo;
const stringURLMap=new Map<string,string>();
const stringURLsMap=new Map<string,{wellknown:string,api:string,cdn:string,gateway:string,login?:string}>();
async function getapiurls(str:string):Promise<{api:string,cdn:string,gateway:string,wellknown:string,login:string}|false>{
if(!URL.canParse(str)){
const val=stringURLMap.get(str);
if(val){
str=val;
}else{
const val=stringURLsMap.get(str);
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
}
}
if(str.at(-1)!=="/"){
str+="/";
}
let api:string;
try{
const info=await fetch(`${str}/.well-known/spacebar`).then(x=>x.json());
api=info.api;
}catch{
return false;
}
const url = new URL(api);
try{
const info=await fetch(`${api}${url.pathname.includes("api") ? "" : "api"}/policies/instance/domains`).then(x=>x.json());
return{
api: info.apiEndpoint,
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
login: url.toString()
};
}catch{
const val=stringURLsMap.get(str);
if(val){
const responce=await fetch(val.api+val.api.endsWith("/")?"":"/"+"ping");
if(responce.ok){
if(val.login){
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}else{
val.login=val.api;
return val as {wellknown:string,api:string,cdn:string,gateway:string,login:string};
}
}
}
return false;
}
}
async function checkInstance(e:string){
const verify=document.getElementById("verify");
try{
verify.textContent="Checking Instance";
const instanceinfo=await getapiurls((instancein as HTMLInputElement).value) as {wellknown:string,api:string,cdn:string,gateway:string,login:string, value:string};
if(instanceinfo){
instanceinfo.value=(instancein as HTMLInputElement).value;
localStorage.setItem("instanceinfo",JSON.stringify(instanceinfo));
verify.textContent="Instance is all good";
if(checkInstance.alt){
checkInstance.alt();
}
setTimeout(_=>{
console.log(verify.textContent);
verify.textContent="";
},3000);
}else{
verify.textContent="Invalid Instance, try again";
}
}catch{
console.log("catch");
verify.textContent="Invalid Instance, try again";
}
}
if(instancein){
console.log(instancein);
instancein.addEventListener("keydown",e=>{
const verify=document.getElementById("verify");
verify.textContent="Waiting to check Instance";
clearTimeout(timeout);
timeout=setTimeout(checkInstance,1000);
});
if(localStorage.getItem("instanceinfo")){
const json=JSON.parse(localStorage.getItem("instanceinfo"));
if(json.value){
(instancein as HTMLInputElement).value=json.value;
}else{
(instancein as HTMLInputElement).value=json.wellknown;
}
}else{
checkInstance("https://spacebar.chat/");
}
}
async function login(username:string, password:string, captcha:string){
if(captcha===""){
captcha=undefined;
}
const options={
method: "POST",
body: JSON.stringify({
login: username,
password,
undelete: false,
captcha_key: captcha
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
}};
try{
const info=JSON.parse(localStorage.getItem("instanceinfo"));
const api=info.login+(info.login.startsWith("/")?"/":"");
return await fetch(api+"/auth/login",options).then(response=>response.json())
.then(response=>{
console.log(response,response.message);
if(response.message==="Invalid Form Body"){
return response.errors.login._errors[0].message;
console.log("test");
}
//this.serverurls||!this.email||!this.token
console.log(response);
if(response.captcha_sitekey){
const capt=document.getElementById("h-captcha");
if(!capt.children.length){
const capty=document.createElement("div");
capty.classList.add("h-captcha");
capty.setAttribute("data-sitekey", response.captcha_sitekey);
const script=document.createElement("script");
script.src="https://js.hcaptcha.com/1/api.js";
capt.append(script);
capt.append(capty);
}else{
eval("hcaptcha.reset()");
}
}else{
console.log(response);
if(response.ticket){
let onetimecode="";
new Dialog(["vdiv",["title","2FA code:"],["textbox","","",function(this:HTMLInputElement){
onetimecode=this.value;
}],["button","","Submit",function(){
fetch(api+"/auth/mfa/totp",{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
code: onetimecode,
ticket: response.ticket,
})
}).then(r=>r.json()).then(response=>{
if(response.message){
alert(response.message);
}else{
console.warn(response);
if(!response.token)return;
adduser({serverurls: JSON.parse(localStorage.getItem("instanceinfo")),email: username,token: response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
}
});
}]]).show();
}else{
console.warn(response);
if(!response.token)return;
adduser({serverurls: JSON.parse(localStorage.getItem("instanceinfo")),email: username,token: response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
window.location.href = redir;
}else{
window.location.href = "/channels/@me";
}
return"";
}
}
});
}catch(error){
console.error("Error:", error);
}
}
async function check(e){
e.preventDefault();
const h=await login(e.srcElement[1].value,e.srcElement[2].value,e.srcElement[3].value);
document.getElementById("wrong").textContent=h;
console.log(h);
}
if(document.getElementById("form")){
document.getElementById("form").addEventListener("submit", check);
}
//this currently does not work, and need to be implemented better at some time.
/*
if ("serviceWorker" in navigator){
navigator.serviceWorker.register("/service.js", {
scope: "/",
}).then((registration) => {
let serviceWorker:ServiceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
console.log("installing");
} else if (registration.waiting) {
serviceWorker = registration.waiting;
console.log("waiting");
} else if (registration.active) {
serviceWorker = registration.active;
console.log("active");
}
if (serviceWorker) {
console.log(serviceWorker.state);
serviceWorker.addEventListener("statechange", (e) => {
console.log(serviceWorker.state);
});
}
})
}
*/
const switchurl=document.getElementById("switch") as HTMLAreaElement;
if(switchurl){
switchurl.href+=window.location.search;
const instance=new URLSearchParams(window.location.search).get("instance");
console.log(instance);
if(instance){
instancein.value=instance;
checkInstance("");
}
}
export{checkInstance};
trimswitcher();
export{mobile, getBulkUsers,getBulkInfo,setTheme,Specialuser,getapiurls,adduser};
const datalist=document.getElementById("instances");
console.warn(datalist);
export function getInstances(){
return instances;
}
fetch("/instances.json").then(_=>_.json()).then((json:{name:string,description?:string,descriptionLong?:string,image?:string,url?:string,display?:boolean,online?:boolean,
uptime:{alltime:number,daytime:number,weektime:number},
urls:{wellknown:string,api:string,cdn:string,gateway:string,login?:string}}[])=>{
instances=json;
if(datalist){
console.warn(json);
if(instancein&&instancein.value===""){
instancein.value=json[0].name;
}
for(const instance of json){
if(instance.display===false){
continue;
}
const option=document.createElement("option");
option.disabled=!instance.online;
option.value=instance.name;
if(instance.url){
stringURLMap.set(option.value,instance.url);
if(instance.urls){
stringURLsMap.set(instance.url,instance.urls);
}
}else if(instance.urls){
stringURLsMap.set(option.value,instance.urls);
}else{
option.disabled=true;
}
if(instance.description){
option.label=instance.description;
}else{
option.label=instance.name;
}
datalist.append(option);
}
checkInstance("");
}
});