mirror of
https://github.com/LukeHagar/idn-admin-console.git
synced 2025-12-06 04:20:02 +00:00
Full Express backend implementation
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,3 +14,5 @@ yarn-error.log
|
||||
pnpm-lock.yaml
|
||||
|
||||
.DS_Store
|
||||
|
||||
out/
|
||||
12
.prettierrc
12
.prettierrc
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"svelteSortOrder": "scripts-markup-styles",
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"trailingComma": "all",
|
||||
"requirePragma": false,
|
||||
"bracketSpacing": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"useTabs": true,
|
||||
"tabWidth": 4,
|
||||
"semi": true
|
||||
}
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"[svelte]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||
}
|
||||
}
|
||||
92
Electron-App/.gitignore
vendored
Normal file
92
Electron-App/.gitignore
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
.DS_Store
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
|
||||
# Vite
|
||||
.vite/
|
||||
|
||||
# Electron-Forge
|
||||
out/
|
||||
31
Electron-App/forge.config.js
Normal file
31
Electron-App/forge.config.js
Normal file
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
packagerConfig: {
|
||||
asar: true,
|
||||
extraResource: ["../Sveltekit-Build"],
|
||||
},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
{
|
||||
name: "@electron-forge/maker-squirrel",
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: "@electron-forge/maker-zip",
|
||||
platforms: ["darwin"],
|
||||
},
|
||||
{
|
||||
name: "@electron-forge/maker-deb",
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: "@electron-forge/maker-rpm",
|
||||
config: {},
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
{
|
||||
name: "@electron-forge/plugin-auto-unpack-natives",
|
||||
config: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
5879
Electron-App/package-lock.json
generated
Normal file
5879
Electron-App/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
Electron-App/package.json
Normal file
35
Electron-App/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "idn-admin-console",
|
||||
"productName": "IdentityNow Admin Console",
|
||||
"version": "1.0.0",
|
||||
"description": "A troubleshooting and administration app for IdentityNow",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
"lint": "echo \"No linting configured\""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": {
|
||||
"name": "Luke Hagar",
|
||||
"email": "luke.hagar@sailpoint.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron-toolkit/utils": "^2.0.1",
|
||||
"electron-log": "^5.0.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"express": "^4.18.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "^7.0.0",
|
||||
"@electron-forge/maker-deb": "^7.0.0",
|
||||
"@electron-forge/maker-rpm": "^7.0.0",
|
||||
"@electron-forge/maker-squirrel": "^7.0.0",
|
||||
"@electron-forge/maker-zip": "^7.0.0",
|
||||
"@electron-forge/plugin-auto-unpack-natives": "^7.0.0",
|
||||
"electron": "27.1.0"
|
||||
}
|
||||
}
|
||||
101
Electron-App/src/index.js
Normal file
101
Electron-App/src/index.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const { is } = require("@electron-toolkit/utils");
|
||||
const handlerPkg = import(
|
||||
is.dev
|
||||
? "../../Sveltekit-Build/src/handler.js"
|
||||
: `file://${path.join(
|
||||
process.resourcesPath,
|
||||
"Sveltekit-Build",
|
||||
"src",
|
||||
"handler.js"
|
||||
)}`
|
||||
);
|
||||
const express = require("express");
|
||||
const log = require("electron-log/main");
|
||||
log.info(
|
||||
"==================================Log Start=================================="
|
||||
);
|
||||
|
||||
const port = 3000;
|
||||
const origin = `http://localhost:${port}`;
|
||||
log.info(`Starting server on ${origin}...`);
|
||||
|
||||
const server = express();
|
||||
|
||||
const createServer = async () => {
|
||||
try {
|
||||
log.info("Starting server...");
|
||||
const { handler } = await handlerPkg;
|
||||
// add a route that lives separately from the SvelteKit app
|
||||
server.get("/healthcheck", (req, res) => {
|
||||
log.info("Healthcheck route hit");
|
||||
res.end("ok");
|
||||
});
|
||||
|
||||
// let SvelteKit handle everything else, including serving prerendered pages and static assets
|
||||
server.use(handler);
|
||||
|
||||
server.listen(3000, () => {
|
||||
console.log(`Server listening on ${origin}`);
|
||||
});
|
||||
} catch (e) {
|
||||
log.info(e);
|
||||
}
|
||||
};
|
||||
|
||||
createServer();
|
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require("electron-squirrel-startup")) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
const createWindow = () => {
|
||||
log.info("Creating window...");
|
||||
// Create the browser window.
|
||||
try {
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, "preload.js"),
|
||||
},
|
||||
});
|
||||
|
||||
log.info("Opening server in window...");
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadURL(origin);
|
||||
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools();
|
||||
} catch (e) {
|
||||
log.info("Error creating window");
|
||||
log.info(e);
|
||||
}
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on("ready", createWindow);
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on("activate", () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and import them here.
|
||||
2
Electron-App/src/preload.js
Normal file
2
Electron-App/src/preload.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// See the Electron documentation for details on how to use preload scripts:
|
||||
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
|
||||
13
Sveltekit-App/.eslintignore
Normal file
13
Sveltekit-App/.eslintignore
Normal file
@@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
30
Sveltekit-App/.eslintrc.cjs
Normal file
30
Sveltekit-App/.eslintrc.cjs
Normal file
@@ -0,0 +1,30 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
10
Sveltekit-App/.gitignore
vendored
Normal file
10
Sveltekit-App/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
1
Sveltekit-App/.npmrc
Normal file
1
Sveltekit-App/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
engine-strict=true
|
||||
13
Sveltekit-App/.prettierignore
Normal file
13
Sveltekit-App/.prettierignore
Normal file
@@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
8
Sveltekit-App/.prettierrc
Normal file
8
Sveltekit-App/.prettierrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
118
Sveltekit-App/.vscode/settings.json
vendored
Normal file
118
Sveltekit-App/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"prettier.documentSelectors": [
|
||||
"**/*.svelte"
|
||||
],
|
||||
"tailwindCSS.classAttributes": [
|
||||
"class",
|
||||
"accent",
|
||||
"active",
|
||||
"aspectRatio",
|
||||
"background",
|
||||
"badge",
|
||||
"bgBackdrop",
|
||||
"bgDark",
|
||||
"bgDrawer",
|
||||
"bgLight",
|
||||
"blur",
|
||||
"border",
|
||||
"button",
|
||||
"buttonAction",
|
||||
"buttonBack",
|
||||
"buttonClasses",
|
||||
"buttonComplete",
|
||||
"buttonDismiss",
|
||||
"buttonNeutral",
|
||||
"buttonNext",
|
||||
"buttonPositive",
|
||||
"buttonTextCancel",
|
||||
"buttonTextConfirm",
|
||||
"buttonTextFirst",
|
||||
"buttonTextLast",
|
||||
"buttonTextNext",
|
||||
"buttonTextPrevious",
|
||||
"buttonTextSubmit",
|
||||
"caretClosed",
|
||||
"caretOpen",
|
||||
"chips",
|
||||
"color",
|
||||
"controlSeparator",
|
||||
"controlVariant",
|
||||
"cursor",
|
||||
"display",
|
||||
"element",
|
||||
"fill",
|
||||
"fillDark",
|
||||
"fillLight",
|
||||
"flex",
|
||||
"gap",
|
||||
"gridColumns",
|
||||
"height",
|
||||
"hover",
|
||||
"inactive",
|
||||
"indent",
|
||||
"justify",
|
||||
"meter",
|
||||
"padding",
|
||||
"position",
|
||||
"regionAnchor",
|
||||
"regionBackdrop",
|
||||
"regionBody",
|
||||
"regionCaption",
|
||||
"regionCaret",
|
||||
"regionCell",
|
||||
"regionChildren",
|
||||
"regionChipList",
|
||||
"regionChipWrapper",
|
||||
"regionCone",
|
||||
"regionContent",
|
||||
"regionControl",
|
||||
"regionDefault",
|
||||
"regionDrawer",
|
||||
"regionFoot",
|
||||
"regionFootCell",
|
||||
"regionFooter",
|
||||
"regionHead",
|
||||
"regionHeadCell",
|
||||
"regionHeader",
|
||||
"regionIcon",
|
||||
"regionInput",
|
||||
"regionInterface",
|
||||
"regionInterfaceText",
|
||||
"regionLabel",
|
||||
"regionLead",
|
||||
"regionLegend",
|
||||
"regionList",
|
||||
"regionListItem",
|
||||
"regionNavigation",
|
||||
"regionPage",
|
||||
"regionPanel",
|
||||
"regionRowHeadline",
|
||||
"regionRowMain",
|
||||
"regionSummary",
|
||||
"regionSymbol",
|
||||
"regionTab",
|
||||
"regionTrail",
|
||||
"ring",
|
||||
"rounded",
|
||||
"select",
|
||||
"shadow",
|
||||
"slotDefault",
|
||||
"slotFooter",
|
||||
"slotHeader",
|
||||
"slotLead",
|
||||
"slotMessage",
|
||||
"slotMeta",
|
||||
"slotPageContent",
|
||||
"slotPageFooter",
|
||||
"slotPageHeader",
|
||||
"slotSidebarLeft",
|
||||
"slotSidebarRight",
|
||||
"slotTrail",
|
||||
"spacing",
|
||||
"text",
|
||||
"track",
|
||||
"transition",
|
||||
"width",
|
||||
"zIndex"
|
||||
]
|
||||
}
|
||||
38
Sveltekit-App/README.md
Normal file
38
Sveltekit-App/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
5107
Sveltekit-App/package-lock.json
generated
Normal file
5107
Sveltekit-App/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
Sveltekit-App/package.json
Normal file
57
Sveltekit-App/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "idn-admin-console",
|
||||
"description": "A troubleshooting and administration app for IdentityNow",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "Luke Hagar",
|
||||
"email": "luke.hagar@sailpoint.com"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "cross-env ORIGIN=http://localhost:3000 vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"test": "vitest",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@skeletonlabs/skeleton": "2.5.0",
|
||||
"@skeletonlabs/tw-plugin": "0.2.4",
|
||||
"@sveltejs/adapter-auto": "^2.0.0",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@sveltejs/kit": "^1.27.4",
|
||||
"@tailwindcss/forms": "0.5.7",
|
||||
"@tailwindcss/typography": "0.5.10",
|
||||
"@types/jsonwebtoken": "^9.0.5",
|
||||
"@types/node": "20.9.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"alasql": "^4.2.2",
|
||||
"autoprefixer": "10.4.16",
|
||||
"axios": "^1.6.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-svelte": "^2.30.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"postcss": "8.4.31",
|
||||
"prettier": "^3.0.0",
|
||||
"prettier-plugin-svelte": "^3.0.0",
|
||||
"sailpoint-api-client": "^1.3.0",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tailwindcss": "3.3.5",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.4.2",
|
||||
"vite-plugin-tailwind-purgecss": "0.1.3",
|
||||
"vitest": "^0.34.6"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "1.5.3"
|
||||
}
|
||||
}
|
||||
6
Sveltekit-App/postcss.config.cjs
Normal file
6
Sveltekit-App/postcss.config.cjs
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
15
Sveltekit-App/src/app.d.ts
vendored
Normal file
15
Sveltekit-App/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
import type { AxiosError } from 'axios';
|
||||
|
||||
declare namespace App {
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
interface Error {
|
||||
message: string;
|
||||
AxiosError?: AxiosError;
|
||||
Error?: Any;
|
||||
}
|
||||
// interface Platform {}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
/* Write your global styles here, in PostCSS syntax */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind variants;
|
||||
|
||||
:root [data-theme='wintry'] {
|
||||
--theme-rounded-base: 5px;
|
||||
--theme-rounded-container: 4px;
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
import axios from 'axios';
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export function generateAuthLink(tenantUrl: string) {
|
||||
return `${tenantUrl}/oauth/authorize?client_id=sailpoint-cli&response_type=code&redirect_uri=http://localhost:3000/callback`;
|
||||
@@ -25,67 +25,61 @@ export type IdnSession = {
|
||||
};
|
||||
|
||||
export async function refreshToken(apiUrl: string, refreshToken: string): Promise<IdnSession> {
|
||||
let url = `${apiUrl}/oauth/token?grant_type=refresh_token&client_id=sailpoint-cli&refresh_token=${refreshToken}`
|
||||
const response = await axios
|
||||
.post(
|
||||
url
|
||||
)
|
||||
.catch(function (err) {
|
||||
const url = `${apiUrl}/oauth/token?grant_type=refresh_token&client_id=sailpoint-cli&refresh_token=${refreshToken}`;
|
||||
const response = await axios.post(url).catch(function (err) {
|
||||
if (err.response) {
|
||||
// Request made and server responded
|
||||
console.log(err.response.data);
|
||||
console.log(err.response.status);
|
||||
console.log(err.response.headers);
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
});
|
||||
// if (response) {
|
||||
// console.log(response.data)
|
||||
// }
|
||||
const idnSession: IdnSession = response!.data as IdnSession;
|
||||
return idnSession
|
||||
return idnSession;
|
||||
}
|
||||
|
||||
export async function getToken(cookies: Cookies): Promise<IdnSession> {
|
||||
|
||||
const idnSession = <IdnSession>JSON.parse(cookies.get('idnSession')!)
|
||||
const session = JSON.parse(cookies.get('session')!)
|
||||
const idnSession = <IdnSession>JSON.parse(cookies.get('idnSession')!);
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
if (!idnSession && session) {
|
||||
throw redirect(302, generateAuthLink(session.tenantUrl))
|
||||
throw redirect(302, generateAuthLink(session.tenantUrl));
|
||||
}
|
||||
if (!idnSession && !session) {
|
||||
throw redirect(302, "/")
|
||||
throw redirect(302, '/');
|
||||
}
|
||||
if (isJwtExpired(idnSession.access_token)) {
|
||||
console.log("refreshing token")
|
||||
const newSession = await refreshToken(session.baseUrl, idnSession.refresh_token)
|
||||
cookies.set("idnSession", JSON.stringify(newSession));
|
||||
return Promise.resolve(newSession)
|
||||
console.log('refreshing token');
|
||||
const newSession = await refreshToken(session.baseUrl, idnSession.refresh_token);
|
||||
cookies.set('idnSession', JSON.stringify(newSession));
|
||||
return Promise.resolve(newSession);
|
||||
} else {
|
||||
console.log("token is good")
|
||||
return Promise.resolve(idnSession)
|
||||
console.log('token is good');
|
||||
return Promise.resolve(idnSession);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isJwtExpired(token: string): boolean {
|
||||
try {
|
||||
const decodedToken: any = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken || !decodedToken.payload || !decodedToken.payload.exp) {
|
||||
// The token is missing the expiration claim ('exp') or is not a valid JWT.
|
||||
return true; // Treat as expired for safety.
|
||||
}
|
||||
|
||||
// Get the expiration timestamp from the token.
|
||||
const expirationTimestamp = decodedToken.payload.exp;
|
||||
|
||||
// Get the current timestamp.
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Check if the token has expired.
|
||||
return currentTimestamp >= expirationTimestamp;
|
||||
const decodedToken = jwt.decode(token, { complete: true });
|
||||
if (!decodedToken || !decodedToken.payload || !decodedToken.payload.exp) {
|
||||
// The token is missing the expiration claim ('exp') or is not a valid JWT.
|
||||
return true; // Treat as expired for safety.
|
||||
}
|
||||
|
||||
// Get the expiration timestamp from the token.
|
||||
const expirationTimestamp = decodedToken.payload.exp;
|
||||
|
||||
// Get the current timestamp.
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
// Check if the token has expired.
|
||||
return currentTimestamp >= expirationTimestamp;
|
||||
} catch (error) {
|
||||
// An error occurred during decoding.
|
||||
return true; // Treat as expired for safety.
|
||||
// An error occurred during decoding.
|
||||
return true; // Treat as expired for safety.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { ManagedClustersBetaApi } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ cookies, params }) {
|
||||
try {
|
||||
// Generic SDK setup
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
|
||||
// Route specific SDK call
|
||||
let api = new ManagedClustersBetaApi(config);
|
||||
|
||||
const val = await api.getManagedCluster({ id: params.clusterID });
|
||||
// console.log(val);
|
||||
return json(val.data);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK source query', err);
|
||||
}
|
||||
}
|
||||
23
Sveltekit-App/src/routes/sailpoint/search/+server.ts
Normal file
23
Sveltekit-App/src/routes/sailpoint/search/+server.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { Paginator, SearchApi, type Search } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function POST({ request, cookies }) {
|
||||
try {
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
const searchJson = await request.json();
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
let api = new SearchApi(config);
|
||||
let search: Search = searchJson;
|
||||
const val = (await Paginator.paginateSearchApi(api, search, 100, 20000)).data;
|
||||
//console.log(val)
|
||||
return json(val);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK search query', err);
|
||||
}
|
||||
}
|
||||
26
Sveltekit-App/src/routes/sailpoint/search/[limit]/+server.ts
Normal file
26
Sveltekit-App/src/routes/sailpoint/search/[limit]/+server.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { Paginator, SearchApi, type Search } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function POST({ request, cookies, params }) {
|
||||
try {
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
const searchJson = await request.json();
|
||||
|
||||
const limit = Number(params.limit || 20000);
|
||||
const increment = limit < 250 ? limit : 250;
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
let api = new SearchApi(config);
|
||||
let search: Search = searchJson;
|
||||
const val = (await Paginator.paginateSearchApi(api, search, increment, limit)).data;
|
||||
//console.log(val)
|
||||
return json(val);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK search query', err);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { Paginator, SourcesApi, type SourcesApiGetSourceRequest } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ cookies, params }) {
|
||||
try {
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
let api = new SourcesApi(config);
|
||||
|
||||
const val = await api.getSource({ id: params.sourceID });
|
||||
// console.log(val);
|
||||
return json(val.data);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK source query', err);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import type { Data, FetchResponse, SourceEvents } from '$lib/Types.js';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { EventDocument, Search, SearchDocument } from 'sailpoint-api-client';
|
||||
|
||||
export const GET = async ({ fetch }) => {
|
||||
const sources = await (await fetch('/api/sailpoint/sources')).json();
|
||||
|
||||
const eventNames: string[] = [
|
||||
'Aggregate Source Account Passed',
|
||||
'Aggregate Source Account Started',
|
||||
'Aggregate Source Entitlement Passed',
|
||||
'Aggregate Source Entitlement Started',
|
||||
];
|
||||
|
||||
const promises = [];
|
||||
const allEvents: EventDocument[] = [];
|
||||
|
||||
for (const source of sources) {
|
||||
for (const event of eventNames) {
|
||||
const search: Search = {
|
||||
indices: ['events'],
|
||||
query: {
|
||||
query: `target.name: "${source.name}" AND name:"${event}"`,
|
||||
},
|
||||
sort: ['created'],
|
||||
};
|
||||
|
||||
promises.push(
|
||||
fetch('/api/sailpoint/search/1', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(search),
|
||||
})
|
||||
.then((response: FetchResponse<SearchDocument>) => response.json())
|
||||
.then((data: Data<SearchDocument>) => {
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises).then((results) => {
|
||||
for (const event of results) {
|
||||
if (event.status == 'fulfilled' && event.value.length > 0) {
|
||||
allEvents.push(event.value[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const events = [];
|
||||
|
||||
for (const source of sources) {
|
||||
const sourceEvents: SourceEvents = {
|
||||
name: source.name,
|
||||
accounts: { started: undefined, passed: undefined },
|
||||
entitlements: { started: undefined, passed: undefined },
|
||||
};
|
||||
|
||||
for (const event of allEvents) {
|
||||
if (event.attributes!.sourceName === source.name) {
|
||||
switch (event.technicalName) {
|
||||
case 'SOURCE_ACCOUNT_AGGREGATE_STARTED':
|
||||
if (!sourceEvents.accounts.started) {
|
||||
sourceEvents.accounts.started = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ACCOUNT_AGGREGATE_PASSED':
|
||||
if (!sourceEvents.accounts.passed) {
|
||||
sourceEvents.accounts.passed = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ENTITLEMENT_AGGREGATE_STARTED':
|
||||
if (!sourceEvents.entitlements.started) {
|
||||
sourceEvents.entitlements.started = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ENTITLEMENT_AGGREGATE_PASSED':
|
||||
if (!sourceEvents.entitlements.passed) {
|
||||
sourceEvents.entitlements.passed = event || undefined;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events.push(sourceEvents);
|
||||
}
|
||||
|
||||
return json({ sources, events });
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
import type { Data, FetchResponse, SourceEvents } from '$lib/Types.js';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { EventDocument, Search, SearchDocument } from 'sailpoint-api-client';
|
||||
|
||||
export const GET = async ({ fetch, params }) => {
|
||||
const source = await (await fetch(`/api/sailpoint/source/${params.sourceID}`)).json();
|
||||
|
||||
const eventNames: string[] = [
|
||||
'Aggregate Source Account Passed',
|
||||
'Aggregate Source Account Started',
|
||||
'Aggregate Source Entitlement Passed',
|
||||
'Aggregate Source Entitlement Started',
|
||||
];
|
||||
|
||||
const promises = [];
|
||||
const allEvents: EventDocument[] = [];
|
||||
|
||||
for (const event of eventNames) {
|
||||
const search: Search = {
|
||||
indices: ['events'],
|
||||
query: {
|
||||
query: `target.name: "${source.name}" AND name:"${event}"`,
|
||||
},
|
||||
sort: ['created'],
|
||||
};
|
||||
|
||||
promises.push(
|
||||
fetch('/api/sailpoint/search/1', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(search),
|
||||
})
|
||||
.then((response: FetchResponse<SearchDocument>) => response.json())
|
||||
.then((data: Data<SearchDocument>) => {
|
||||
return data;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises).then((results) => {
|
||||
for (const event of results) {
|
||||
if (event.status == 'fulfilled' && event.value.length > 0) {
|
||||
allEvents.push(event.value[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const sourceEvents: SourceEvents = {
|
||||
name: source.name,
|
||||
accounts: { started: undefined, passed: undefined },
|
||||
entitlements: { started: undefined, passed: undefined },
|
||||
};
|
||||
|
||||
for (const event of allEvents) {
|
||||
if (event.attributes!.sourceName === source.name) {
|
||||
switch (event.technicalName) {
|
||||
case 'SOURCE_ACCOUNT_AGGREGATE_STARTED':
|
||||
if (!sourceEvents.accounts.started) {
|
||||
sourceEvents.accounts.started = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ACCOUNT_AGGREGATE_PASSED':
|
||||
if (!sourceEvents.accounts.passed) {
|
||||
sourceEvents.accounts.passed = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ENTITLEMENT_AGGREGATE_STARTED':
|
||||
if (!sourceEvents.entitlements.started) {
|
||||
sourceEvents.entitlements.started = event || undefined;
|
||||
}
|
||||
break;
|
||||
case 'SOURCE_ENTITLEMENT_AGGREGATE_PASSED':
|
||||
if (!sourceEvents.entitlements.passed) {
|
||||
sourceEvents.entitlements.passed = event || undefined;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return json({ source, sourceEvents });
|
||||
};
|
||||
21
Sveltekit-App/src/routes/sailpoint/sources/+server.ts
Normal file
21
Sveltekit-App/src/routes/sailpoint/sources/+server.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { Paginator, SourcesApi } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ cookies }) {
|
||||
try {
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
let api = new SourcesApi(config);
|
||||
const val = (await Paginator.paginate(api, api.listSources, { limit: 100 }, 10)).data;
|
||||
//console.log(val)
|
||||
return json(val);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK source query', err);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { HandleError } from '$lib/Errors.js';
|
||||
import { createConfiguration } from '$lib/sailpoint/sdk';
|
||||
import { getToken } from '$lib/utils/oauth';
|
||||
import { json } from '@sveltejs/kit';
|
||||
import { Paginator, SourcesApi } from 'sailpoint-api-client';
|
||||
|
||||
/** @type {import('./$types').RequestHandler} */
|
||||
export async function GET({ cookies, params }) {
|
||||
try {
|
||||
const session = JSON.parse(cookies.get('session')!);
|
||||
const idnSession = await getToken(cookies);
|
||||
|
||||
const limit = Number(params.limit || 20000);
|
||||
const increment = limit < 250 ? limit : 250;
|
||||
|
||||
const config = createConfiguration(session.baseUrl, idnSession.access_token);
|
||||
let api = new SourcesApi(config);
|
||||
const val = (await Paginator.paginate(api, api.listSources, { limit }, increment)).data;
|
||||
//console.log(val)
|
||||
return json(val);
|
||||
} catch (err) {
|
||||
HandleError('issue arose during SDK source query', err);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 259 KiB |
21
Sveltekit-App/svelte.config.js
Normal file
21
Sveltekit-App/svelte.config.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
extensions: ['.svelte'],
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: [vitePreprocess()],
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter({ out: '../Sveltekit-Build/src' }),
|
||||
csrf: {
|
||||
checkOrigin: false
|
||||
}
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@@ -1,31 +1,28 @@
|
||||
import { join } from 'path';
|
||||
import type { Config } from 'tailwindcss';
|
||||
|
||||
// 1. Import the Skeleton plugin
|
||||
import forms from '@tailwindcss/forms';
|
||||
import typography from '@tailwindcss/typography';
|
||||
import { skeleton } from '@skeletonlabs/tw-plugin';
|
||||
|
||||
const config = {
|
||||
// 2. Opt for dark mode to be handled via the class method
|
||||
export default {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
'./src/**/*.{html,js,svelte,ts}',
|
||||
// 3. Append the path to the Skeleton package
|
||||
join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}'),
|
||||
join(require.resolve('@skeletonlabs/skeleton'), '../**/*.{html,js,svelte,ts}')
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
extend: {}
|
||||
},
|
||||
plugins: [
|
||||
// 4. Append the Skeleton plugin (after other plugins)
|
||||
forms,
|
||||
typography,
|
||||
skeleton({
|
||||
themes: {
|
||||
preset: [
|
||||
{ name: 'wintry', enhancements: true },
|
||||
{ name: 'skeleton', enhancements: true },
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
{ name: 'skeleton', enhancements: true }
|
||||
]
|
||||
}
|
||||
})
|
||||
]
|
||||
} satisfies Config;
|
||||
|
||||
export default config;
|
||||
18
Sveltekit-App/tsconfig.json
Normal file
18
Sveltekit-App/tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
||||
14
Sveltekit-App/vite.config.ts
Normal file
14
Sveltekit-App/vite.config.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { purgeCss } from 'vite-plugin-tailwind-purgecss';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), purgeCss()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
origin: 'http://localhost:3000'
|
||||
}
|
||||
});
|
||||
8
Sveltekit-Build/package.json
Normal file
8
Sveltekit-Build/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "built-app",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"start": "node ./src"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.sliding-text.svelte-1fdnh10.svelte-1fdnh10.svelte-1fdnh10{display:inline-block;position:relative;line-height:1em;height:1em}.sliding-text.svelte-1fdnh10>span.svelte-1fdnh10.svelte-1fdnh10{height:1em;display:inline-block;overflow-y:hidden}.sliding-text.svelte-1fdnh10>span.svelte-1fdnh10>span.svelte-1fdnh10{text-align:center;transition:all var(--interval) var(--ease);position:relative;height:100%;white-space:pre;top:calc(var(--index) * -2em)}.skills.svelte-1285lhc{display:flex;justify-items:start;align-items:center;flex-wrap:wrap}.custom-skill{display:inline-block;text-align:center}
|
||||
@@ -0,0 +1 @@
|
||||
.progress-bar.svelte-sfm816{padding-top:calc(50vh - 4.5rem - 200px);padding-left:calc(50% - 4.5rem)}
|
||||
@@ -0,0 +1 @@
|
||||
.animIndeterminate.svelte-meqa4r{transform-origin:0% 50%;animation:svelte-meqa4r-animIndeterminate 2s infinite linear}@keyframes svelte-meqa4r-animIndeterminate{0%{transform:translate(0) scaleX(0)}40%{transform:translate(0) scaleX(.4)}to{transform:translate(100%) scaleX(.5)}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.progress-bar.svelte-sfm816{padding-top:calc(50vh - 4.5rem - 200px);padding-left:calc(50% - 4.5rem)}
|
||||
@@ -0,0 +1 @@
|
||||
.sliding-text.svelte-1fdnh10.svelte-1fdnh10.svelte-1fdnh10{display:inline-block;position:relative;line-height:1em;height:1em}.sliding-text.svelte-1fdnh10>span.svelte-1fdnh10.svelte-1fdnh10{height:1em;display:inline-block;overflow-y:hidden}.sliding-text.svelte-1fdnh10>span.svelte-1fdnh10>span.svelte-1fdnh10{text-align:center;transition:all var(--interval) var(--ease);position:relative;height:100%;white-space:pre;top:calc(var(--index) * -2em)}.skills.svelte-1285lhc{display:flex;justify-items:start;align-items:center;flex-wrap:wrap}.custom-skill{display:inline-block;text-align:center}
|
||||
@@ -0,0 +1,3 @@
|
||||
import{a3 as X,a4 as z,s as Y,e as x,i as B,z as O,d as v,v as Z,w as $,x as V,y as W,f as H,l as M,a as J,g as L,h as N,m as j,c as R,j as C,A as E,u as ee,B as te,n as P,a5 as ae,D as ne,H as oe,F as le}from"./scheduler.10bc074a.js";import{S as ie,i as re}from"./index.7b3d626a.js";import{w as se}from"./index.09b1afef.js";const ce=se(void 0);function ue(n,e){if(!window.isSecureContext){console.error("Clipboard action failed: app not running in secure context, see: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard");return}const t=()=>{n.dispatchEvent(new CustomEvent("copyComplete"))},a=()=>{if(typeof e=="object"){if("element"in e){const l=document.querySelector(`[data-clipboard="${e.element}"]`);if(!l)throw new Error(`Missing HTMLElement with an attribute of [data-clipboard="${e.element}"]`);D(l.innerHTML,"text/html").then(t);return}if("input"in e){const l=document.querySelector(`[data-clipboard="${e.input}"]`);if(!l)throw new Error(`Missing HTMLInputElement with an attribute of [data-clipboard="${e.input}"]`);D(l.value).then(t);return}}D(e).then(t)};return n.addEventListener("click",a),{update(l){e=l},destroy(){n.removeEventListener("click",a)}}}async function D(n,e="text/plain"){navigator.clipboard.write?await navigator.clipboard.write([new ClipboardItem({[e]:new Blob([n],{type:e}),"text/plain":new Blob([n],{type:"text/plain"})})]):await new Promise(t=>{t(navigator.clipboard.writeText(String(n)))})}function Q(n){const e=n-1;return e*e*e+1}function Ce(n,{delay:e=0,duration:t=400,easing:a=X}={}){const l=+getComputedStyle(n).opacity;return{delay:e,duration:t,easing:a,css:r=>`opacity: ${r*l}`}}function ve(n,{delay:e=0,duration:t=400,easing:a=Q,x:l=0,y:r=0,opacity:y=0}={}){const i=getComputedStyle(n),b=+i.opacity,s=i.transform==="none"?"":i.transform,u=b*(1-y),[p,g]=z(l),[m,d]=z(r);return{delay:e,duration:t,easing:a,css:(_,k)=>`
|
||||
transform: ${s} translate(${(1-_)*p}${g}, ${(1-_)*m}${d});
|
||||
opacity: ${b-u*k}`}}function Ee(n,{delay:e=0,duration:t=400,easing:a=Q,axis:l="y"}={}){const r=getComputedStyle(n),y=+r.opacity,i=l==="y"?"height":"width",b=parseFloat(r[i]),s=l==="y"?["top","bottom"]:["left","right"],u=s.map(c=>`${c[0].toUpperCase()}${c.slice(1)}`),p=parseFloat(r[`padding${u[0]}`]),g=parseFloat(r[`padding${u[1]}`]),m=parseFloat(r[`margin${u[0]}`]),d=parseFloat(r[`margin${u[1]}`]),_=parseFloat(r[`border${u[0]}Width`]),k=parseFloat(r[`border${u[1]}Width`]);return{delay:e,duration:t,easing:a,css:c=>`overflow: hidden;opacity: ${Math.min(c*20,1)*y};${i}: ${c*b}px;padding-${s[0]}: ${c*p}px;padding-${s[1]}: ${c*g}px;margin-${s[0]}: ${c*m}px;margin-${s[1]}: ${c*d}px;border-${s[0]}-width: ${c*_}px;border-${s[1]}-width: ${c*k}px;`}}function we(n,e){const{transition:t,params:a,enabled:l}=e;return l?t(n,a):"duration"in a?t(n,{duration:0}):{duration:0}}function G(n){let e,t,a,l=K(n[0])+"",r,y,i,b=(n[7]?n[4]:n[3])+"",s,u,p,g,m,d,_,k,c,F;function S(o,f){return o[6]?fe:de}let w=S(n),h=w(n);return{c(){e=H("div"),t=H("header"),a=H("span"),r=M(l),y=J(),i=H("button"),s=M(b),g=J(),m=H("pre"),d=H("code"),h.c(),this.h()},l(o){e=L(o,"DIV",{class:!0,"data-testid":!0});var f=N(e);t=L(f,"HEADER",{class:!0});var T=N(t);a=L(T,"SPAN",{class:!0});var U=N(a);r=j(U,l),U.forEach(v),y=R(T),i=L(T,"BUTTON",{class:!0});var A=N(i);s=j(A,b),A.forEach(v),T.forEach(v),g=R(f),m=L(f,"PRE",{class:!0});var I=N(m);d=L(I,"CODE",{class:!0});var q=N(d);h.l(q),q.forEach(v),I.forEach(v),f.forEach(v),this.h()},h(){C(a,"class","codeblock-language"),C(i,"class",u="codeblock-btn "+n[2]),C(t,"class","codeblock-header "+he),C(d,"class",_="codeblock-code language-"+n[0]+" lineNumbers"),C(m,"class","codeblock-pre "+pe),C(e,"class",k="codeblock "+n[8]),C(e,"data-testid","codeblock")},m(o,f){B(o,e,f),E(e,t),E(t,a),E(a,r),E(t,y),E(t,i),E(i,s),E(e,g),E(e,m),E(m,d),h.m(d,null),c||(F=[ee(i,"click",n[9]),te(p=ue.call(null,i,n[1]))],c=!0)},p(o,f){f&1&&l!==(l=K(o[0])+"")&&P(r,l),f&152&&b!==(b=(o[7]?o[4]:o[3])+"")&&P(s,b),f&4&&u!==(u="codeblock-btn "+o[2])&&C(i,"class",u),p&&ae(p.update)&&f&2&&p.update.call(null,o[1]),w===(w=S(o))&&h?h.p(o,f):(h.d(1),h=w(o),h&&(h.c(),h.m(d,null))),f&1&&_!==(_="codeblock-code language-"+o[0]+" lineNumbers")&&C(d,"class",_),f&256&&k!==(k="codeblock "+o[8])&&C(e,"class",k)},d(o){o&&v(e),h.d(),c=!1,ne(F)}}}function de(n){let e=n[1].trim()+"",t;return{c(){t=M(e)},l(a){t=j(a,e)},m(a,l){B(a,t,l)},p(a,l){l&2&&e!==(e=a[1].trim()+"")&&P(t,e)},d(a){a&&v(t)}}}function fe(n){let e,t;return{c(){e=new oe(!1),t=x(),this.h()},l(a){e=le(a,!1),t=x(),this.h()},h(){e.a=t},m(a,l){e.m(n[5],a,l),B(a,t,l)},p(a,l){l&32&&e.p(a[5])},d(a){a&&(v(t),e.d())}}}function be(n){let e,t=n[0]&&n[1]&&G(n);return{c(){t&&t.c(),e=x()},l(a){t&&t.l(a),e=x()},m(a,l){t&&t.m(a,l),B(a,e,l)},p(a,[l]){a[0]&&a[1]?t?t.p(a,l):(t=G(a),t.c(),t.m(e.parentNode,e)):t&&(t.d(1),t=null)},i:O,o:O,d(a){a&&v(e),t&&t.d(a)}}}const me="overflow-hidden shadow",he="text-xs text-white/50 uppercase flex justify-between items-center p-2 pl-4",pe="whitespace-pre-wrap break-all p-4 pt-1";function K(n){return n==="js"?"javascript":n==="ts"?"typescript":n==="shell"?"terminal":n}function _e(n,e,t){let a,l;Z(n,ce,o=>t(17,l=o));const r=$();let{language:y="plaintext"}=e,{code:i=""}=e,{lineNumbers:b=!1}=e,{background:s="bg-neutral-900/90"}=e,{blur:u=""}=e,{text:p="text-sm"}=e,{color:g="text-white"}=e,{rounded:m="rounded-container-token"}=e,{shadow:d="shadow"}=e,{button:_="btn btn-sm variant-soft !text-white"}=e,{buttonLabel:k="Copy"}=e,{buttonCopied:c="👍"}=e,F=!1,S=i,w=!1;function h(){t(7,w=!0),setTimeout(()=>{t(7,w=!1)},2e3),r("copy")}return n.$$set=o=>{t(19,e=V(V({},e),W(o))),"language"in o&&t(0,y=o.language),"code"in o&&t(1,i=o.code),"lineNumbers"in o&&t(10,b=o.lineNumbers),"background"in o&&t(11,s=o.background),"blur"in o&&t(12,u=o.blur),"text"in o&&t(13,p=o.text),"color"in o&&t(14,g=o.color),"rounded"in o&&t(15,m=o.rounded),"shadow"in o&&t(16,d=o.shadow),"button"in o&&t(2,_=o.button),"buttonLabel"in o&&t(3,k=o.buttonLabel),"buttonCopied"in o&&t(4,c=o.buttonCopied)},n.$$.update=()=>{n.$$.dirty&131075&&l!==void 0&&(t(5,S=l.highlight(i,{language:y}).value.trim()),t(6,F=!0)),n.$$.dirty&1056&&b&&(t(5,S=S.replace(/^/gm,()=>'<span class="line"></span> ')),t(6,F=!0)),t(8,a=`${me} ${s} ${u} ${p} ${g} ${m} ${d} ${e.class??""}`)},e=W(e),[y,i,_,k,c,S,F,w,a,h,b,s,u,p,g,m,d,l]}class Se extends ie{constructor(e){super(),re(this,e,_e,be,Y,{language:0,code:1,lineNumbers:10,background:11,blur:12,text:13,color:14,rounded:15,shadow:16,button:2,buttonLabel:3,buttonCopied:4})}}export{Se as C,ve as a,we as d,Ce as f,Ee as s};
|
||||
@@ -0,0 +1 @@
|
||||
import{s as N,H as Q,e as M,a as R,f as z,T as I,U as X,F as Y,d as k,c as Z,g as B,h as T,V as H,j as c,A as S,i as P,u as w,z as j,D as x,v as p,o as $,x as q,y as V,W as tt,E as C}from"./scheduler.10bc074a.js";import{S as et,i as it}from"./index.7b3d626a.js";import{s as st,m as F,a as U,g as at,b as lt}from"./ProgressBar.svelte_svelte_type_style_lang.ae9fcbec.js";function ct(t){let s,a=`<script nonce="%sveltekit.nonce%">(${st.toString()})();<\/script>`,o,f,i,u,h,g,b,r,_,d,m,v;return{c(){s=new Q(!1),o=M(),f=R(),i=z("div"),u=z("div"),h=I("svg"),g=I("path"),this.h()},l(l){const n=X("svelte-gewkj4",document.head);s=Y(n,!1),o=M(),n.forEach(k),f=Z(l),i=B(l,"DIV",{class:!0,role:!0,"aria-label":!0,"aria-checked":!0,title:!0,tabindex:!0});var L=T(i);u=B(L,"DIV",{class:!0});var D=T(u);h=H(D,"svg",{class:!0,xmlns:!0,viewBox:!0});var y=T(h);g=H(y,"path",{d:!0}),T(g).forEach(k),y.forEach(k),D.forEach(k),L.forEach(k),this.h()},h(){s.a=o,c(g,"d",b=t[1]?t[5].sun:t[5].moon),c(h,"class",r="lightswitch-icon "+t[2]),c(h,"xmlns","http://www.w3.org/2000/svg"),c(h,"viewBox","0 0 512 512"),c(u,"class",_="lightswitch-thumb "+t[3]),c(i,"class",d="lightswitch-track "+t[4]),c(i,"role","switch"),c(i,"aria-label","Light Switch"),c(i,"aria-checked",t[1]),c(i,"title",t[0]),c(i,"tabindex","0")},m(l,n){s.m(a,document.head),S(document.head,o),P(l,f,n),P(l,i,n),S(i,u),S(u,h),S(h,g),m||(v=[w(i,"click",t[6]),w(i,"click",t[19]),w(i,"keydown",ut),w(i,"keydown",t[20]),w(i,"keyup",t[21]),w(i,"keypress",t[22])],m=!0)},p(l,[n]){n&2&&b!==(b=l[1]?l[5].sun:l[5].moon)&&c(g,"d",b),n&4&&r!==(r="lightswitch-icon "+l[2])&&c(h,"class",r),n&8&&_!==(_="lightswitch-thumb "+l[3])&&c(u,"class",_),n&16&&d!==(d="lightswitch-track "+l[4])&&c(i,"class",d),n&2&&c(i,"aria-checked",l[1]),n&1&&c(i,"title",l[0])},i:j,o:j,d(l){l&&(s.d(),k(f),k(i)),k(o),m=!1,x(v)}}}const rt="cursor-pointer",nt="aspect-square scale-[0.8] flex justify-center items-center",ht="w-[70%] aspect-square";function ut(t){["Enter","Space"].includes(t.code)&&(t.preventDefault(),t.currentTarget.click())}function ot(t,s,a){let o,f,i,u,h,g,b,r;p(t,F,e=>a(1,r=e));let{title:_="Toggle light or dark mode."}=s,{bgLight:d="bg-surface-50"}=s,{bgDark:m="bg-surface-900"}=s,{fillLight:v="fill-surface-50"}=s,{fillDark:l="fill-surface-900"}=s,{width:n="w-12"}=s,{height:L="h-6"}=s,{ring:D="ring-[1px] ring-surface-500/30"}=s,{rounded:y="rounded-token"}=s;const E="transition-all duration-[200ms]",A={sun:"M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM352 256c0 53-43 96-96 96s-96-43-96-96s43-96 96-96s96 43 96 96zm32 0c0-70.7-57.3-128-128-128s-128 57.3-128 128s57.3 128 128 128s128-57.3 128-128z",moon:"M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"};function K(){tt(F,r=!r,r),lt(r),U(r)}$(()=>{"modeCurrent"in localStorage||U(at())});function O(e){C.call(this,t,e)}function W(e){C.call(this,t,e)}function G(e){C.call(this,t,e)}function J(e){C.call(this,t,e)}return t.$$set=e=>{a(24,s=q(q({},s),V(e))),"title"in e&&a(0,_=e.title),"bgLight"in e&&a(7,d=e.bgLight),"bgDark"in e&&a(8,m=e.bgDark),"fillLight"in e&&a(9,v=e.fillLight),"fillDark"in e&&a(10,l=e.fillDark),"width"in e&&a(11,n=e.width),"height"in e&&a(12,L=e.height),"ring"in e&&a(13,D=e.ring),"rounded"in e&&a(14,y=e.rounded)},t.$$.update=()=>{t.$$.dirty&386&&a(18,o=r===!0?d:m),t.$$.dirty&386&&a(17,f=r===!0?m:d),t.$$.dirty&2&&a(16,i=r===!0?"translate-x-[100%]":""),t.$$.dirty&1538&&a(15,u=r===!0?v:l),a(4,h=`${rt} ${E} ${n} ${L} ${D} ${y} ${o} ${s.class??""}`),t.$$.dirty&217088&&a(3,g=`${nt} ${E} ${L} ${y} ${f} ${i}`),t.$$.dirty&32768&&a(2,b=`${ht} ${u}`)},s=V(s),[_,r,b,g,h,A,K,d,m,v,l,n,L,D,y,u,i,f,o,O,W,G,J]}class mt extends et{constructor(s){super(),it(this,s,ot,ct,N,{title:0,bgLight:7,bgDark:8,fillLight:9,fillDark:10,width:11,height:12,ring:13,rounded:14})}}export{mt as L};
|
||||
@@ -0,0 +1 @@
|
||||
import{s as i,f as m,g as l,h as c,d as o,j as p,i as _,z as f}from"./scheduler.10bc074a.js";import{S as d,i as u,b as g,d as h,m as v,a as $,t as y,e as P}from"./index.7b3d626a.js";import"./ProgressBar.svelte_svelte_type_style_lang.ae9fcbec.js";import{P as k}from"./Table.f5db4492.js";function b(n){let e,s,r;return s=new k({props:{stroke:100,meter:"stroke-primary-500",track:"stroke-primary-500/30",class:"progress-bar"}}),{c(){e=m("div"),g(s.$$.fragment),this.h()},l(t){e=l(t,"DIV",{class:!0});var a=c(e);h(s.$$.fragment,a),a.forEach(o),this.h()},h(){p(e,"class","progress-bar svelte-sfm816")},m(t,a){_(t,e,a),v(s,e,null),r=!0},p:f,i(t){r||($(s.$$.fragment,t),r=!0)},o(t){y(s.$$.fragment,t),r=!1},d(t){t&&o(e),P(s)}}}class w extends d{constructor(e){super(),u(this,e,null,b,i,{})}}export{w as P};
|
||||
@@ -0,0 +1 @@
|
||||
import{w as M,r as S}from"./index.09b1afef.js";import{a6 as P}from"./scheduler.10bc074a.js";const l={};function h(e){return e==="local"?localStorage:sessionStorage}function i(e,t,s){const r=(s==null?void 0:s.serializer)??JSON,m=(s==null?void 0:s.storage)??"local";function u(n,c){h(m).setItem(n,r.stringify(c))}if(!l[e]){const n=M(t,o=>{const a=h(m).getItem(e);a&&o(r.parse(a));{const g=d=>{d.key===e&&o(d.newValue?r.parse(d.newValue):null)};return window.addEventListener("storage",g),()=>window.removeEventListener("storage",g)}}),{subscribe:c,set:f}=n;l[e]={set(o){u(e,o),f(o)},update(o){const a=o(P(n));u(e,a),f(a)},subscribe:c}}return l[e]}const L=i("modeOsPrefers",!1),v=i("modeUserPrefers",void 0),p=i("modeCurrent",!1);function C(){const e=window.matchMedia("(prefers-color-scheme: light)").matches;return L.set(e),e}function b(e){v.set(e)}function I(e){const t=document.documentElement.classList,s="dark";e===!0?t.remove(s):t.add(s),p.set(e)}function O(){const e=document.documentElement.classList,t=localStorage.getItem("modeUserPrefers")==="false",s=!("modeUserPrefers"in localStorage),r=window.matchMedia("(prefers-color-scheme: dark)").matches;t||s&&r?e.add("dark"):e.remove("dark")}const w="(prefers-reduced-motion: reduce)";function E(){return window.matchMedia(w).matches}const R=S(E(),e=>{{const t=r=>{e(r.matches)},s=window.matchMedia(w);return s.addEventListener("change",t),()=>{s.removeEventListener("change",t)}}});export{I as a,b,C as g,i as l,p as m,R as p,O as s};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user