import { v1 } from 'jsr:@std/uuid';
import { deleteCookie, getCookies, setCookie } from "jsr:@std/http";
import { MemCache } from "https://viapak.xyz/@utils/memcache";
import { logger } from "https://viapak.xyz/@utils/logger";
import { RequestManager } from "../modules/request_manager.ts";
import { User } from "./user.ts";
export class Draco {
private static Cache = new MemCache<Draco.User>();
private static _HOST: string
static get HOST() { return this._HOST }
private static _API_KEY: string
static get API_KEY() { return this._API_KEY }
private static _PERMISSIONS: Record<string, any>
static get Permissions() { return this._PERMISSIONS }
private static _COOKIE_NAME: string
static get COOKIE_NAME() { return this._COOKIE_NAME }
private static _initialized = false
static readonly RequestManager = new RequestManager(this);
static SendRequest<T = any>(...params: Parameters<RequestManager['Send']>) {
if(!this._initialized) throw new Error('Draco not configured. [Draco.Configure()]');
return this.RequestManager.Send<T>(...params);
}
static async Configure(options: Draco.Options) {
this.log("Configured");
this._HOST = options.host || Deno.env.get('DRACO_HOST') || Deno.env.get('DRACO_URL')!;
this._API_KEY = options.api_key || Deno.env.get('DRACO_API_KEY')!;
this._PERMISSIONS = Object.freeze(options.permissions);
this._COOKIE_NAME = options.cookie_name || Deno.env.get("DRACO_COOKIE") || "draco_uuid";
if(!this.HOST) throw new Error('Draco host not set');
if(!this.API_KEY) throw new Error('Draco api key not set');
this._initialized = true;
await this.AnnouncePermissions();
}
static async AnnouncePermissions() {
const ok = (await this.SendRequest("announce-permissions", {
permissions: Object.values(this._PERMISSIONS)
})) !== null;
if(ok) {
this.log("%cConnected", 'color: green');
this.log(`Permissions Synchronized`);
}
return ok;
}
static LogoutRedirect() {
const headers = new Headers({
Location: "/"
});
deleteCookie(headers, this.COOKIE_NAME);
setCookie(headers, {
name: this.COOKIE_NAME,
value: v1.generate()
})
return new Response(null, {
headers,
status: 307
})
}
static async LoginRedirect(cookie_uuid: Draco.Login.Request['cookie_uuid'], redirect_url: Draco.Login.Request['redirect_url']) {
const response = await this.SendRequest<Draco.Login.Response>("login-request", { cookie_uuid, redirect_url });
const user_redirect_url = response?.redirect_url;
return new Response(null, {
headers: {
Location: user_redirect_url || '/'
},
status: 307
})
}
static GetCookie(request: Request) {
return getCookies(request.headers)[this.COOKIE_NAME] || false;
}
/**
* Returns a `Response` to initialize a new Draco cookie.
*
* **WARNING**: Only use if `GetCookie` returns false!
*/
static CookieRedirect(request: Request) {
const url = new URL(request.url);
const cookie = getCookies(request.headers)[this.COOKIE_NAME];
const headers = new Headers({
Location: request.url
});
if(!cookie) {
setCookie(headers, {
name: this.COOKIE_NAME,
value: v1.generate(),
path: "/",
domain: url.hostname,
expires: Date.now() + TWO_WEEKS,
httpOnly: true,
secure: /^https/.test(url.protocol),
})
}
return new Response(null, {
headers,
status: 307
})
}
static async GetUser(cookie_uuid?: string, force?: boolean): Promise<Draco.User.Class | null>
static async GetUser(request: Request, force?: boolean): Promise<Draco.User.Class | null>
static async GetUser(cookie_or_request: string | undefined | Request, force?: boolean) {
let cookie_uuid: string | undefined;
if(cookie_or_request instanceof Request) {
cookie_uuid = this.GetCookie(cookie_or_request) || undefined;
} else cookie_uuid = cookie_or_request;
if(!cookie_uuid) return null;
if(this.Cache.has(cookie_uuid) && !force) return new User(this.Cache.get(cookie_uuid)!);
const { user } = (await this.SendRequest<{ user: Draco.User }>('refresh-token', { cookie_uuid })) ?? {};
if(!user) return null;
if(user?.image_path?.indexOf('/') == 0) {
user.image_path = this.HOST.replace(/\/+$/, '') + user.image_path;
}
this.Cache.set(cookie_uuid, user);
return new User(user);
}
static async LogEvent(cookie: string | undefined, event: Draco.Event): Promise<boolean>
static async LogEvent(request: Request, event: Draco.Event): Promise<boolean>
static async LogEvent(cookie_or_request: string | undefined | Request, event: Draco.Event): Promise<boolean> {
let cookie_uuid: string | undefined;
if(cookie_or_request instanceof Request) {
cookie_uuid = this.GetCookie(cookie_or_request) || undefined;
} else cookie_uuid = cookie_or_request;
if(!cookie_uuid) return false;
const success = (await this.SendRequest("log-event", {
cookie_uuid,
event: event.type,
information: event.information,
ip_address: event.ip_address,
} satisfies Draco.Event.Request)) !== null;
return success;
}
static log = logger({
glyph: () => this._initialized ? '⛊' : '⛉',
glyph_color: "#00aaff",
name: "Draco",
name_color: "#00aaff",
})
}
const TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;