UNPKG

altcha

Version:

GDPR compliant, self-hosted CAPTCHA alternative.

5 lines (4 loc) 9.16 kB
(function(c,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(c=typeof globalThis<"u"?globalThis:c||self,a(c["[name]"]={}))})(this,function(c){"use strict";var me=Object.defineProperty;var j=c=>{throw TypeError(c)};var ge=(c,a,d)=>a in c?me(c,a,{enumerable:!0,configurable:!0,writable:!0,value:d}):c[a]=d;var y=(c,a,d)=>ge(c,typeof a!="symbol"?a+"":a,d),_=(c,a,d)=>a.has(c)||j("Cannot "+d);var b=(c,a,d)=>(_(c,a,"read from private field"),d?d.call(c):a.get(c)),P=(c,a,d)=>a.has(c)?j("Cannot add the same private member more than once"):a instanceof WeakSet?a.add(c):a.set(c,d);var h=(c,a,d)=>(_(c,a,"access private method"),d);var g,w,l,k,H,R,K,O,q,M,v,B,G,$;const a={generateKey:d,exportKey:Y,importKey:D,decrypt:J,encrypt:V};async function d(t=256){return crypto.subtle.generateKey({name:"AES-GCM",length:t},!0,["encrypt","decrypt"])}async function Y(t){return new Uint8Array(await crypto.subtle.exportKey("raw",t))}async function D(t){return crypto.subtle.importKey("raw",t,{name:"AES-GCM"},!0,["encrypt","decrypt"])}async function V(t,n,e=16){const i=crypto.getRandomValues(new Uint8Array(e));return{encrypted:new Uint8Array(await crypto.subtle.encrypt({name:"AES-GCM",iv:i},t,n)),iv:i}}async function J(t,n,e){return new Uint8Array(await crypto.subtle.decrypt({name:"AES-GCM",iv:e},t,n))}function X(t,n=!1){return n&&(t=t.replace(/_/g,"/").replace(/-/g,"+")+"=".repeat(3-(3+t.length)%4)),Uint8Array.from(atob(t),e=>e.charCodeAt(0))}function S(t,n=!1){const e=btoa(String.fromCharCode(...t));return n?e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,""):e}function x(t,n=80){let e="";for(;t.length>0;)e+=t.slice(0,n)+` `,t=t.slice(n);return e}function L(t){return X(t.split(/\r?\n/).filter(n=>!n.startsWith("-----")).join(""))}const m="RSA-OAEP",E="SHA-256",Q=2048,W=new Uint8Array([1,0,1]),Z={generateKeyPair:ee,encrypt:te,decrypt:ne,exportPrivateKey:C,exportPrivateKeyPem:ie,exportPublicKey:U,exportPublicKeyPem:re,exportPublicKeyFromPrivateKey:oe,importPrivateKey:N,importPrivateKeyPem:se,importPublicKey:I,importPublicKeyPem:T};async function ee(){return crypto.subtle.generateKey({name:m,modulusLength:Q,publicExponent:W,hash:E},!0,["encrypt","decrypt"])}async function te(t,n){return new Uint8Array(await crypto.subtle.encrypt({name:m},t,n))}async function ne(t,n){return new Uint8Array(await crypto.subtle.decrypt({name:m},t,n))}async function U(t){return new Uint8Array(await crypto.subtle.exportKey("spki",t))}async function C(t){return new Uint8Array(await crypto.subtle.exportKey("pkcs8",t))}async function re(t){return`-----BEGIN PUBLIC KEY----- `+x(S(await U(t)),64)+"-----END PUBLIC KEY-----"}async function ie(t){return`-----BEGIN PRIVATE KEY----- `+x(S(await C(t)),64)+"-----END PRIVATE KEY-----"}async function I(t){return crypto.subtle.importKey("spki",t,{name:m,hash:E},!0,["encrypt"])}async function T(t){return I(L(t))}async function N(t){return crypto.subtle.importKey("pkcs8",t,{name:m,hash:E},!0,["decrypt"])}async function se(t){return N(L(t))}async function oe(t){const n=await crypto.subtle.exportKey("jwk",t);delete n.d,delete n.dp,delete n.dq,delete n.q,delete n.qi,n.key_ops=["encrypt"];const e=await crypto.subtle.importKey("jwk",n,{name:m,hash:E},!0,["encrypt"]);return U(e)}const ae=new Uint8Array([1,0,1]),ce=256,le=16;async function pe(t,n,e={}){const{aesIVLength:i=le,aesKeyLength:r=ce}=e,o=await a.generateKey(r),{encrypted:p,iv:s}=await a.encrypt(o,n,i),u=await Z.encrypt(t,await a.exportKey(o));return new Uint8Array([...ae,...new Uint8Array([u.length]),...new Uint8Array([s.length]),...u,...s,...p])}class A{constructor(n){this.context=n}static register(n){typeof globalThis.altchaPlugins!="object"&&(globalThis.altchaPlugins=[]),globalThis.altchaPlugins.includes(n)||globalThis.altchaPlugins.push(n)}destroy(){}onErrorChange(n){}onStateChange(n){}}y(A,"pluginName");class F extends A{constructor(e){super(e);P(this,l);y(this,"pendingFiles",[]);y(this,"uploadHandles",[]);y(this,"elForm");P(this,g,h(this,l,q).bind(this));P(this,w,h(this,l,M).bind(this));this.elForm=this.context.el.closest("form"),this.elForm&&(this.elForm.addEventListener("change",b(this,g)),this.elForm.addEventListener("submit",b(this,w),{capture:!0}))}addFile(e,i){this.pendingFiles.find(([r,o])=>r===e&&o===i)||this.pendingFiles.push([e,i])}destroy(){this.elForm&&(this.elForm.removeEventListener("change",b(this,g)),this.elForm.removeEventListener("submit",b(this,w)))}async uploadPendingFiles(){var i;const e=async()=>{const r=this.pendingFiles[0];if(r&&await h(this,l,v).call(this,h(this,l,H).call(this,r)),this.pendingFiles.length)return e()};await e(),this.pendingFiles.length===0&&(h(this,l,k).call(this),(i=this.elForm)==null||i.requestSubmit())}}g=new WeakMap,w=new WeakMap,l=new WeakSet,k=function(){var i,r,o;const e=this.uploadHandles.reduce((p,s)=>(p[s.fieldName]||(p[s.fieldName]=[]),s.fileId&&p[s.fieldName].push(s.fileId),p),{});for(const p in e){const s=document.createElement("input");s.name=p,s.type="hidden",s.value=e[p].join(","),(r=(i=this.elForm)==null?void 0:i.querySelector(`[name="${p}"]`))==null||r.setAttribute("disabled","disabled"),(o=this.elForm)==null||o.appendChild(s)}},H=function(e){const i=this.pendingFiles.findIndex(([o,p])=>o===e[0]&&p===e[1]);if(i<0)throw new Error("Cannot create upload handle.");const r=new ue(e[0],e[1]);return this.uploadHandles.push(r),this.pendingFiles.splice(i,1),h(this,l,R).call(this,r),h(this,l,K).call(this),r},R=function(e){this.context.dispatch("upload",{handle:e})},K=function(){const e=this.pendingFiles.reduce((r,[o,p])=>r+p.size,0)+this.uploadHandles.reduce((r,{uploadSize:o})=>r+o,0),i=this.uploadHandles.reduce((r,{loaded:o})=>r+o,0);this.context.dispatch("uploadprogress",{bytesLoaded:i,bytesTotal:e,pendingFiles:this.pendingFiles,uploadHandles:this.uploadHandles})},O=function(){if(this.elForm){const e=this.elForm.getAttribute("action");return this.elForm.getAttribute("data-upload-url")||e+"/file"}return null},q=function(e){const i=e.target;if(i&&i.type==="file"){const r=i.files;if(r!=null&&r.length)for(const o of r)this.addFile(i.name,o)}},M=function(e){this.pendingFiles.length&&(e.preventDefault(),e.stopPropagation(),this.uploadPendingFiles())},v=async function(e,i){const r=h(this,l,O).call(this);if(!r)throw new Error("Upload url not specified.");const o={"content-type":"application/json"};i&&(o.authorization="Altcha payload="+i);const p=await fetch(r,{body:JSON.stringify({name:e.file.name||"file",size:e.file.size,type:e.file.type||"application/octet-stream"}),credentials:"include",headers:o,method:"POST"});if(p.status===401)return h(this,l,B).call(this,p,e);if(p.status!==200)throw new Error(`Unexpected server response ${p.status}.`);const s=await p.json();let u=e.file;if(s.encrypted&&s.encryptionPublicKey){const f=await T(s.encryptionPublicKey),de=await new Response(new ReadableStream({async start(z){const ye=e.file.stream().getReader();for(;;){const{done:he,value:fe}=await ye.read();if(he)break;z.enqueue(fe)}z.close()}})).arrayBuffer();u=await pe(f,new Uint8Array(de))}return e.uploadSize=u instanceof Uint8Array?u.byteLength:e.file.size,await h(this,l,$).call(this,s.uploadUrl,e,u,{"content-type":e.file.type||"application/octet-stream"}),s.finalizeUrl&&await h(this,l,G).call(this,s.finalizeUrl,e.uploadSize),e.fileId=s.fileId,e.resolve({encrypted:s.encrypted,fileId:s.fileId}),e.promise},B=async function(e,i){var r;try{const o=e.headers.get("www-authenticate"),p=(r=o==null?void 0:o.match(/challenge=(.*),/))==null?void 0:r[1];if(!p)throw new Error("Unable to retrieve altcha challenge from www-authenticate header.");const s=JSON.parse(p);if(s&&"challenge"in s){const{solution:u}=await this.context.solve(s);if(u&&"number"in u)return h(this,l,v).call(this,i,btoa(JSON.stringify({...s,number:u.number})));throw new Error("Invalid challenge solution.")}}catch(o){throw this.context.log(o),new Error("Unable to solve altcha challenge for upload.")}},G=async function(e,i){const r=await fetch(e,{body:JSON.stringify({uploadedBytes:i}),headers:{"content-type":"application/json"},method:"POST"});if(r.status>204)throw new Error(`Unexpected server response ${r.status}.`);return!0},$=async function(e,i,r,o={}){return new Promise((p,s)=>{const u=new XMLHttpRequest;i.controller.signal.addEventListener("abort",()=>{u.abort()}),u.upload.addEventListener("progress",f=>{i.setProgress(f.loaded),h(this,l,K).call(this)}),u.addEventListener("error",f=>{s(new Error("Upload failed."))}),u.addEventListener("load",()=>{p(void 0)}),u.open("PUT",e);for(const f in o)u.setRequestHeader(f,o[f]);u.send(r)})},y(F,"pluginName","upload");class ue{constructor(n,e){y(this,"controller",new AbortController);y(this,"promise");y(this,"fileId");y(this,"loaded",0);y(this,"progress",0);y(this,"uploadSize",0);y(this,"resolve");y(this,"reject");this.fieldName=n,this.file=e,this.uploadSize=this.file.size,this.promise=new Promise((i,r)=>{this.resolve=i,this.reject=r})}abort(){this.controller.abort()}setProgress(n){this.loaded=n,this.progress=this.file.size&&n?Math.min(1,n/this.file.size):0}}A.register(F),c.PluginUpload=F,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})});