Compare commits

..

14 Commits

55 changed files with 18133 additions and 12549 deletions

View File

@ -1,6 +0,0 @@
query Continents {
countries_continents {
name
code
}
}

View File

@ -1,7 +0,0 @@
query Countries($filter: countries_CountryFilterInput) {
countries_countries(filter: $filter) {
code
name
capital
}
}

View File

@ -1,6 +0,0 @@
query Dragons {
spacex_dragons {
name
active
}
}

View File

@ -0,0 +1,8 @@
query {
system_db_files {
id
title
type
filename_disk
}
}

View File

@ -0,0 +1,11 @@
query {
system_db_users_me {
id
first_name
last_name
avatar {
id
}
external_identifier
}
}

View File

@ -1,5 +1,5 @@
query Projects {
directus_projects {
query {
db_projects {
id
text
}

View File

@ -1,5 +1,5 @@
query Todos {
directus_todos {
query {
db_todos {
id
task
user_created {

View File

@ -0,0 +1,27 @@
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({
address: z.string(),
}),
handler: async ({ input }) => {
console.log('Making request with input:', input);
const { data } = await axios.get('https://api.gnosisscan.io/api', {
params: {
module: 'account',
action: 'balance',
address: input.address,
tag: 'latest',
apikey: process.env.GNOSISSCAN_API,
},
timeout: 10000,
});
console.log('Received response:', data);
return {
balance: parseFloat(data.result),
};
},
});

View File

@ -0,0 +1,8 @@
query {
db_bookmarks {
id
name
url
tags
}
}

View File

@ -0,0 +1,20 @@
// .wundergraph/operations/getChatwootContacts.ts
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({
}),
handler: async () => {
console.log('Making request to Chatwoot API');
const { data } = await axios.get(`https://chatwoot.andert.me/api/v1/accounts/1/contacts`, {
headers: {
api_access_token: process.env.CHATWOOT_API_ACCESS_TOKEN
},
});
return data;
},
});

View File

@ -0,0 +1,19 @@
// .wundergraph/operations/getChatwootConversations.ts
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({}),
handler: async () => {
console.log('Making request to Chatwoot API');
const { data } = await axios.get('https://chatwoot.andert.me/api/v1/accounts/1/conversations?status=open&sort_by=last_activity_at', {
headers: {
api_access_token: process.env.CHATWOOT_API_ACCESS_TOKEN
},
});
return data;
},
});

View File

@ -0,0 +1,20 @@
// .wundergraph/operations/getChatwootMessages.ts
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({
conversationId: z.string(),
}),
handler: async ({ input }) => {
console.log('Making request to Chatwoot API');
const { data } = await axios.get(`https://chatwoot.andert.me/api/v1/accounts/1/conversations/${input.conversationId}/messages`, {
headers: {
api_access_token: process.env.CHATWOOT_API_ACCESS_TOKEN
},
});
return data;
},
});

View File

@ -0,0 +1,70 @@
// .wundergraph/operations/getPaperless.ts
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({}),
handler: async () => {
console.log('Making request to Paperless API');
const { data } = await axios.get('https://paperless.andert.me/api/documents/', {
headers: {
Authorization: process.env.PAPERLESS_TOKEN,
},
});
// Add download link, thumbnail link, preview link, PDF data, and metadata to each document
const documentsWithLinksDataAndMetadata = await Promise.all(data.results.map(async doc => {
const response = await axios.get(`https://paperless.andert.me/api/documents/${doc.id}/preview/`, {
responseType: 'arraybuffer',
headers: {
Authorization: process.env.PAPERLESS_TOKEN,
},
});
const pdfData = Buffer.from(response.data, 'binary').toString('base64');
let correspondent = null;
if (doc.correspondent) {
const correspondentResponse = await axios.get(`https://paperless.andert.me/api/correspondents/${doc.correspondent}/`, {
headers: {
Authorization: process.env.PAPERLESS_TOKEN,
},
});
correspondent = correspondentResponse.data;
}
let tags = [];
if (doc.tags) {
const tagsResponse = await Promise.all(doc.tags.map(tag => axios.get(`https://paperless.andert.me/api/tags/${tag}/`, {
headers: {
Authorization: process.env.PAPERLESS_TOKEN,
},
})));
tags = tagsResponse.map(response => response.data);
}
let documentType = null;
if (doc.document_type) {
const documentTypeResponse = await axios.get(`https://paperless.andert.me/api/document_types/${doc.document_type}/`, {
headers: {
Authorization: process.env.PAPERLESS_TOKEN,
},
});
documentType = documentTypeResponse.data;
}
return {
...doc,
downloadLink: `https://paperless.andert.me/api/documents/${doc.id}/download/`,
thumbnailLink: `https://paperless.andert.me/api/documents/${doc.id}/thumb/`,
previewLink: `https://paperless.andert.me/api/documents/${doc.id}/preview/`,
pdfData,
correspondent,
tags,
documentType,
};
}));
return documentsWithLinksDataAndMetadata;
},
});

View File

@ -0,0 +1,27 @@
import { createOperation, z } from '../generated/wundergraph.factory';
import axios from 'axios';
export default createOperation.query({
input: z.object({
address: z.string(),
}),
handler: async ({ input }) => {
const { data } = await axios.get('https://api.gnosisscan.io/api', {
params: {
module: 'account',
action: 'txlist',
address: input.address,
startblock: 0,
endblock: 'latest',
sort: 'desc',
apikey: process.env.GNOSISSCAN_API,
},
});
return {
transactions: data.result.map(transaction => ({
...transaction,
timestamp: new Date(transaction.timeStamp * 1000).toISOString(),
})),
};
},
});

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,9 @@ type Query {
projects(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [projects!]!
projects_by_id(id: ID!): projects
projects_aggregated(groupBy: [String], filter: projects_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [projects_aggregated!]!
bookmarks(filter: bookmarks_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [bookmarks!]!
bookmarks_by_id(id: ID!): bookmarks
bookmarks_aggregated(groupBy: [String], filter: bookmarks_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [bookmarks_aggregated!]!
todos(filter: todos_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [todos!]!
todos_by_id(id: ID!): todos
todos_aggregated(groupBy: [String], filter: todos_filter, limit: Int, offset: Int, page: Int, search: String, sort: [String]): [todos_aggregated!]!
@ -10,23 +13,29 @@ type Query {
type Mutation {
create_projects_items(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [create_projects_input!]): [projects!]!
create_projects_item(data: create_projects_input!): projects
create_bookmarks_items(filter: bookmarks_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [create_bookmarks_input!]): [bookmarks!]!
create_bookmarks_item(data: create_bookmarks_input!): bookmarks
create_todos_items(filter: todos_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [create_todos_input!]): [todos!]!
create_todos_item(data: create_todos_input!): todos
update_projects_items(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, ids: [ID]!, data: update_projects_input!): [projects!]!
update_projects_batch(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [update_projects_input!]): [projects!]!
update_projects_item(id: ID!, data: update_projects_input!): projects
update_bookmarks_items(filter: bookmarks_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, ids: [ID]!, data: update_bookmarks_input!): [bookmarks!]!
update_bookmarks_batch(filter: bookmarks_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [update_bookmarks_input!]): [bookmarks!]!
update_bookmarks_item(id: ID!, data: update_bookmarks_input!): bookmarks
update_todos_items(filter: todos_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, ids: [ID]!, data: update_todos_input!): [todos!]!
update_todos_batch(filter: todos_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String, data: [update_todos_input!]): [todos!]!
update_todos_item(id: ID!, data: update_todos_input!): todos
delete_projects_items(ids: [ID]!): delete_many
delete_projects_item(id: ID!): delete_one
delete_bookmarks_items(ids: [ID]!): delete_many
delete_bookmarks_item(id: ID!): delete_one
delete_todos_items(ids: [ID]!): delete_many
delete_todos_item(id: ID!): delete_one
}
type Subscription {
projects_mutated(event: EventEnum): projects_mutated
todos_mutated(event: EventEnum): todos_mutated
directus_dashboards_mutated(event: EventEnum): directus_dashboards_mutated
directus_activity_mutated(event: EventEnum): directus_activity_mutated
directus_notifications_mutated(event: EventEnum): directus_notifications_mutated
@ -44,6 +53,8 @@ type Subscription {
directus_users_mutated(event: EventEnum): directus_users_mutated
directus_shares_mutated(event: EventEnum): directus_shares_mutated
directus_webhooks_mutated(event: EventEnum): directus_webhooks_mutated
bookmarks_mutated(event: EventEnum): bookmarks_mutated
todos_mutated(event: EventEnum): todos_mutated
}
"""The `Boolean` scalar type represents `true` or `false`."""
@ -92,6 +103,58 @@ enum EventEnum {
delete
}
type bookmarks {
id: ID!
status: String
sort: Int
user_created(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
date_created: Date
date_created_func: datetime_functions
user_updated(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
date_updated: Date
date_updated_func: datetime_functions
url: String
name: String
tags: JSON
tags_func: count_functions
}
type bookmarks_aggregated {
group: JSON
countAll: Int
count: bookmarks_aggregated_count
countDistinct: bookmarks_aggregated_count
avg: bookmarks_aggregated_fields
sum: bookmarks_aggregated_fields
avgDistinct: bookmarks_aggregated_fields
sumDistinct: bookmarks_aggregated_fields
min: bookmarks_aggregated_fields
max: bookmarks_aggregated_fields
}
type bookmarks_aggregated_count {
id: Int
status: Int
sort: Int
user_created: Int
date_created: Int
user_updated: Int
date_updated: Int
url: Int
name: Int
tags: Int
}
type bookmarks_aggregated_fields {
sort: Float
}
type bookmarks_mutated {
key: ID!
event: EventEnum
data: bookmarks
}
type count_functions {
count: Int
}
@ -557,15 +620,16 @@ type projects_mutated {
type todos {
id: ID!
status: String
sort: Int
user_created(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
date_created: Date
date_created_func: datetime_functions
user_updated(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
date_updated: Date
date_updated_func: datetime_functions
enddate: Date
enddate_func: datetime_functions
task: String
type: String
}
type todos_aggregated {
@ -573,27 +637,17 @@ type todos_aggregated {
countAll: Int
count: todos_aggregated_count
countDistinct: todos_aggregated_count
avg: todos_aggregated_fields
sum: todos_aggregated_fields
avgDistinct: todos_aggregated_fields
sumDistinct: todos_aggregated_fields
min: todos_aggregated_fields
max: todos_aggregated_fields
}
type todos_aggregated_count {
id: Int
status: Int
sort: Int
user_created: Int
date_created: Int
user_updated: Int
date_updated: Int
enddate: Int
task: Int
}
type todos_aggregated_fields {
sort: Float
type: Int
}
type todos_mutated {
@ -602,6 +656,24 @@ type todos_mutated {
data: todos
}
input bookmarks_filter {
id: string_filter_operators
status: string_filter_operators
sort: number_filter_operators
user_created: directus_users_filter
date_created: date_filter_operators
date_created_func: datetime_function_filter_operators
user_updated: directus_users_filter
date_updated: date_filter_operators
date_updated_func: datetime_function_filter_operators
url: string_filter_operators
name: string_filter_operators
tags: string_filter_operators
tags_func: count_function_filter_operators
_and: [bookmarks_filter]
_or: [bookmarks_filter]
}
input boolean_filter_operators {
_eq: Boolean
_neq: Boolean
@ -613,6 +685,19 @@ input count_function_filter_operators {
count: number_filter_operators
}
input create_bookmarks_input {
id: ID
status: String
sort: Int
user_created: create_directus_users_input
date_created: Date
user_updated: create_directus_users_input
date_updated: Date
url: String
name: String
tags: JSON
}
input create_directus_files_input {
id: ID
storage: String!
@ -692,13 +777,13 @@ input create_projects_input {
input create_todos_input {
id: ID
status: String
sort: Int
user_created: create_directus_users_input
date_created: Date
user_updated: create_directus_users_input
date_updated: Date
enddate: Date
task: String
type: String
}
input date_filter_operators {
@ -979,19 +1064,33 @@ input string_filter_operators {
input todos_filter {
id: string_filter_operators
status: string_filter_operators
sort: number_filter_operators
user_created: directus_users_filter
date_created: date_filter_operators
date_created_func: datetime_function_filter_operators
user_updated: directus_users_filter
date_updated: date_filter_operators
date_updated_func: datetime_function_filter_operators
enddate: date_filter_operators
enddate_func: datetime_function_filter_operators
task: string_filter_operators
type: string_filter_operators
_and: [todos_filter]
_or: [todos_filter]
}
input update_bookmarks_input {
id: ID
status: String
sort: Int
user_created: update_directus_users_input
date_created: Date
user_updated: update_directus_users_input
date_updated: Date
url: String
name: String
tags: JSON
}
input update_directus_files_input {
id: ID
storage: String
@ -1071,11 +1170,11 @@ input update_projects_input {
input update_todos_input {
id: ID
status: String
sort: Int
user_created: update_directus_users_input
date_created: Date
user_updated: update_directus_users_input
date_updated: Date
enddate: Date
task: String
type: String
}

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,16 @@ export async function fetchSchemas() {
}
});
// Fetch the GraphQL SDL schema
const { data: systemSchema } = await axios.get(`${serverUrl}/server/specs/graphql/system`, {
headers: {
'Authorization': process.env.DIRECTUS
}
});
// Save the schema to a file
fs.writeFileSync('./.wundergraph/schemas/directus.graphql', schema);
fs.writeFileSync('./.wundergraph/schemas/directus_system.graphql', systemSchema);
}
fetchSchemas().catch(console.error);

View File

@ -0,0 +1,181 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "JSON Placeholder API",
"description": "See https://jsonplaceholder.typicode.com/"
},
"servers": [
{
"url": "https://jsonplaceholder.typicode.com"
}
],
"paths": {
"/posts": {
"get": {
"description": "Returns all posts",
"tags": [
"Posts"
],
"operationId": "getPosts",
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostsList"
}
}
}
}
}
}
},
"/users": {
"get": {
"description": "Returns all users",
"tags": [
"Users"
],
"operationId": "getUsers",
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserList"
}
}
}
}
}
}
},
"/users/{id}": {
"get": {
"description": "Returns a user by id",
"tags": [
"Users"
],
"operationId": "getUser",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "The user id.",
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"404": {
"description": "User not found"
}
}
}
},
"/posts/{id}": {
"get": {
"description": "Returns a post by id",
"tags": [
"Posts"
],
"operationId": "getPost",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"description": "The user id.",
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Post"
}
}
}
},
"404": {
"description": "Post not found"
}
}
}
}
},
"components": {
"schemas": {
"PostsList": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Post"
}
},
"UserList": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
},
"Post": {
"type": "object",
"required": [
"id",
"userId",
"title"
],
"properties": {
"id": {
"type": "integer"
},
"userId": {
"type": "integer"
},
"title": {
"type": "string"
}
}
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"username": {
"type": "string"
},
"email": {
"type": "string"
}
}
}
}
}
}

View File

@ -7,28 +7,36 @@ import dotenv from 'dotenv';
dotenv.config();
const directusSchema = fs.readFileSync(path.join(path.resolve(), './schemas/directus.graphql'), 'utf8');
const directusSystemSchema = fs.readFileSync(path.join(path.resolve(), './schemas/directus_system.graphql'), 'utf8');
const countries = introspect.graphql({
apiNamespace: 'countries',
url: 'https://countries.trevorblades.com/',
});
const spaceX = introspect.graphql({
apiNamespace: 'spacex',
url: 'https://spacex-api.fly.dev/graphql/',
});
const directus = introspect.graphql({
apiNamespace: 'directus',
const db = introspect.graphql({
apiNamespace: 'db',
loadSchemaFromString: directusSchema,
url: 'https://directus.andert.me/graphql',
headers: (builder) => builder
.addStaticHeader('Authorization', new EnvironmentVariable('DIRECTUS', process.env.DIRECTUS))
});
const placeholder = introspect.openApiV2({
apiNamespace: 'placeholder',
source: {
kind: "file",
filePath: "./schemas/placeholder.json"
},
baseURL: 'https://jsonplaceholder.typicode.com',
});
const system_db = introspect.graphql({
apiNamespace: 'system_db',
loadSchemaFromString: directusSystemSchema,
url: 'https://directus.andert.me/graphql/system',
headers: (builder) => builder
.addStaticHeader('Authorization', new EnvironmentVariable('DIRECTUS', process.env.DIRECTUS))
});
// configureWunderGraph emits the configuration
configureWunderGraphApplication({
apis: [countries, spaceX, directus],
apis: [db, system_db, placeholder],
server,
operations,
generate: {
@ -59,12 +67,12 @@ configureWunderGraphApplication({
tokenBased: {
providers: [
{
userInfoEndpoint: 'http://localhost:3000/jwt/wunderauth',
userInfoEndpoint: 'http://localhost:3000/server/wundergraph',
},
],
},
},
authorization: {
roles: ['admin'],
roles: ['owner'],
},
});

View File

@ -21,16 +21,19 @@
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.5.0",
"@tauri-apps/cli": "^1.4.0",
"@tailwindcss/forms": "^0.5.6",
"@tauri-apps/cli": "2.0.0-alpha.14",
"@types/cookie": "^0.5.1",
"@types/js-cookie": "^3.0.3",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^20.5.8",
"autoprefixer": "^10.4.15",
"concurrently": "^7.6.0",
"dayjs": "^1.11.9",
"postcss": "^8.4.29",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"svelte-time": "^0.8.0",
"tailwindcss": "^3.3.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
@ -48,12 +51,14 @@
"@lit-protocol/lit-auth-client": "^2.2.50",
"@lit-protocol/lit-node-client": "^2.2.50",
"@lit-protocol/pkp-client": "^2.2.50",
"@lit-protocol/pkp-ethers": "^2.2.54",
"@lit-protocol/types": "^2.2.50",
"@tanstack/svelte-query": "^4.29.1",
"@wagmi/core": "^1.3.9",
"@wundergraph/sdk": "^0.174.5",
"@wundergraph/svelte-query": "^0.3.10",
"@xstate/svelte": "^2.1.0",
"ai": "^2.2.13",
"axios": "^1.4.0",
"cookie": "^0.5.0",
"dotenv": "^16.3.1",
@ -63,8 +68,11 @@
"jsonwebtoken": "^9.0.1",
"jwks-rsa": "^3.0.1",
"node-jose": "^2.2.0",
"openai": "^4.8.0",
"path": "^0.12.7",
"sqlite3": "^5.1.6",
"svelte-kit-cookie-session": "^4.0.0",
"svelte-pdf-simple": "^2.0.0",
"url": "^0.11.1",
"xstate": "^4.38.2"
},

12381
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,74 @@
<script>
let accs = [
// Mock data
{ id: 1, name: "ACC 1" },
{ id: 2, name: "ACC 2" },
{ id: 3, name: "ACC 3" },
];
<script lang="ts">
import Cookies from "js-cookie";
import {
createACC,
deleteACC,
} from "./services/mutateAccessControlConditions.ts";
let signingConditions =
JSON.parse(localStorage.getItem("signingConditions")) || [];
function createNewACC() {
// Logic for creating a new ACC goes here
console.log("Create new ACC button clicked");
let newParameter = ":userAddress";
let newComparator = "=";
let newValue = "";
async function handleCreateNewACC() {
await createACC(newParameter, newComparator, newValue);
signingConditions =
JSON.parse(localStorage.getItem("signingConditions")) || [];
Cookies.set("signingConditions", JSON.stringify(signingConditions));
}
async function handleDeleteACC(index) {
await deleteACC(index);
signingConditions =
JSON.parse(localStorage.getItem("signingConditions")) || [];
}
</script>
<div class="p-12">
<h1>Access Control Conditions</h1>
{#each accs as acc (acc.id)}
<p>{acc.name}</p>
{/each}
<button on:click={createNewACC}>Create New ACC</button>
{#each signingConditions as condition, index (index)}
{#each condition.accs as acc}
<div
class="card"
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"
>
<div class="p-4">
<h2 class="text-xl">
<b>{condition.resourceId.baseUrl}{condition.resourceId.path}</b>
</h2>
<p>
{acc.parameters.join(", ")}
{acc.returnValueTest.comparator}
{acc.returnValueTest.value}
</p>
</div>
<button
on:click={() => handleDeleteACC(index)}
class="btn variant-filled-error mt-2">Delete ACC</button
>
</div>
{/each}
{/each}
<div class="mt-4 flex">
<input
bind:value={newParameter}
placeholder="Parameter"
class="input mr-2"
style="width: max-content; max-width: 125px;"
readonly
/>
<input
bind:value={newComparator}
placeholder="Comparator"
class="input mr-2"
style="width: max-content; max-width: 36px;"
readonly
/>
<input
bind:value={newValue}
placeholder="Value"
class="input mr-2 flex-grow"
/>
<button on:click={handleCreateNewACC} class="btn variant-filled flex-grow"
>Create New ACC</button
>
</div>
<style>
/* Add your Tailwind CSS classes here */
</style>

49
src/lib/Ai.svelte Normal file
View File

@ -0,0 +1,49 @@
<script>
import { useChat } from "ai/svelte";
import Icon from "@iconify/svelte";
const { input, handleSubmit, messages } = useChat();
</script>
<div class="chat-grid h-full p-4">
<ul class="overflow-auto flex flex-col p-4 text-sm">
{#each $messages as message}
<li class="message {message.role === 'user' ? 'user' : 'assistant'} p-2">
{message.content}
</li>
{/each}
</ul>
<div class="w-full">
<form on:submit={handleSubmit} class="flex items-center">
<input bind:value={$input} class="input flex-grow mr-4" type="text" />
<button class="btn-icon variant-filled-success" type="submit">
<div class="px-4">
<Icon icon="carbon:send-alt-filled" class="" width="24" height="24" />
</div>
</button>
</form>
</div>
</div>
<style>
.chat-grid {
display: grid;
grid-template-rows: 1fr auto;
height: 100%;
}
.message {
max-width: 80%;
padding: px;
margin: 10px;
border-radius: 10px;
}
.user {
align-self: flex-end;
background-color: #0d6efd;
color: white;
}
.assistant {
align-self: flex-start;
background-color: lightblue;
}
</style>

View File

@ -0,0 +1,5 @@
<script>
export let json;
</script>
<pre>{JSON.stringify(json, null, 2)}</pre>

14
src/lib/MailViewer.svelte Normal file
View File

@ -0,0 +1,14 @@
<script>
function shadowroot(node, { html }) {
node.attachShadow({ mode: "open" }).innerHTML = html;
return {
update({ html }) {
node.shadowRoot.innerHTML = html;
},
};
}
export let html = "<h1>Some custom HTML</h1>";
</script>
<div use:shadowroot={{ html }} />

View File

@ -9,10 +9,12 @@
startSignIn as startSignInService,
} from "./services/signInWithGoogle";
import { getDrawerStore } from "@skeletonlabs/skeleton";
import { goto } from "$app/navigation";
const { state, send } = useMachine(walletMachine);
const drawerStore = getDrawerStore();
let search = "";
$: walletState.set($state.context);
$: {
@ -65,7 +67,7 @@
<div class="flex flex-col items-center w-1/3">
<button
on:click={startSignIn}
class="flex items-center justify-center w-full py-2 text-white bg-blue-500 rounded hover:bg-blue-700"
class="btn variant-filled flex items-center justify-center py-2"
>
<span class="mr-2"><Icon icon="flat-color-icons:google" /></span>
<span>Sign in with Google</span>
@ -73,32 +75,42 @@
</div>
</div>
{:else if $state.context.pkps}
<div
class="flex flex-col items-center p-3 space-y-4 bg-white bg-opacity-75 rounded-t-lg shadow-md"
>
<div class="flex flex-col items-center p-3 space-y-4">
<div class="flex items-center justify-between w-full space-x-4">
<div class="w-full h-full overflow-hidden grid grid-cols-6">
<aside class="col-span-1">
<a href="/me" on:click|preventDefault={() => goto("/me")}>
<div class="flex items-center space-x-2">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-12 h-12 text-gray-500"
/>
<div>
<p class="text-sm">
<span class="font-semibold">Address:</span>
{$state.context.pkps[0].ethAddress}
</p>
<p class="text-xs">
<span class="font-semibold">Provider:</span>
{$state.context.providerName}
</p>
<div class="text-sm">
<div class="font-semibold">Address</div>
{$state.context.pkps[0].ethAddress.substring(0, 8) + "..."}
</div>
</div>
</div>
</a>
</aside>
<div class="col-span-5 w-full">
<div class="flex justify-end space-x-4">
<button
on:click={signRequestTrigger}
type="button"
class="btn variant-filled">SignRequest</button
>
<button type="button" class="btn variant-filled" on:click={clearSession}
>Logout</button
<button
type="button"
class="btn variant-filled"
on:click={clearSession}>Logout</button
>
</div>
</div>
</div>
</div>
</div>
{:else if $state.matches("sessionExpired")}
<div class="p-10 bg-white">
<p>Error creating session. Please try again.</p>
@ -106,9 +118,9 @@
</div>
{/if}
{:else}
<div class="p-10 bg-white rounded-full">
<div class="p-5 bg-white rounded-full animate-spin">
<Icon icon="la:spinner" width="100" height="100" />
<div class="flex justify-center items-center w-full pb-4">
<div class="animate-spin">
<Icon icon="la:spinner" width="48" height="48" />
</div>
</div>
{/if}

View File

@ -0,0 +1,10 @@
<div class="py-6 px-12 w-full h-full">
<header>
<h3 class="text-xl font-bold mb-4 uppercase">
<slot name="header" />
</h3>
</header>
<main class="w-full h-full overflow-hidden">
<slot name="main" />
</main>
</div>

View File

@ -49,9 +49,11 @@ const walletMachine = createMachine({
},
onDone: {
target: 'creatingSession',
actions: assign({
actions: [
assign({
pkps: (_, event) => event.data,
}),
],
},
onError: 'authenticated',
},

8
src/lib/mockApps.js Normal file
View File

@ -0,0 +1,8 @@
export const mockApps = [
{ name: 'cloud' },
{ name: 'pass'},
{ name: 'directus' },
{ name: 'penpot'},
{ name: 'paperless'}
];

View File

@ -27,12 +27,14 @@ export const createJWT = async () => {
contractAddress: '',
standardContractType: '',
chain: 'xdai',
method: 'eth_getBalance',
parameters: [':userAddress', 'latest'],
method: '',
parameters: [
':userAddress',
],
returnValueTest: {
comparator: '>=',
value: '1000000000000',
},
comparator: '=',
value: '0x4b975F10baf1153A5CC688B52d55809cd2d8BB57'
}
},
];

View File

@ -0,0 +1,66 @@
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import type { AccsEVMParams } from "@lit-protocol/types";
export const createACC = async (newParameter, newComparator, newValue) => {
const litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
await litNodeClient.connect();
const me = JSON.parse(localStorage.getItem('me'));
if (!me || !me.sessionSigs) {
throw new Error('No sessionSigs found in local storage');
}
const newACC = {
conditionType: "evmBasic",
contractAddress: "",
standardContractType: "",
chain: "xdai",
method: "",
parameters: [newParameter],
returnValueTest: {
comparator: newComparator,
value: newValue,
},
};
const resourceId = {
baseUrl: "https://localhost:3000",
path: "/server/wundergraph",
orgId: "°",
role: "owner",
extraData: "",
};
const sessionSigs = me.sessionSigs;
await litNodeClient.saveSigningCondition({
unifiedAccessControlConditions: [newACC],
sessionSigs,
resourceId,
chain: "litSessionSign",
});
const jwt = await litNodeClient.getSignedToken({
unifiedAccessControlConditions: [newACC],
chain: 'xdai',
sessionSigs,
resourceId
});
let signingConditions = JSON.parse(localStorage.getItem("signingConditions")) || [];
signingConditions = [
...signingConditions,
{
accs: [newACC],
resourceId,
jwt,
},
];
localStorage.setItem("signingConditions", JSON.stringify(signingConditions));
};
export const deleteACC = async (index) => {
let signingConditions = JSON.parse(localStorage.getItem("signingConditions")) || [];
signingConditions = signingConditions.filter((_, i) => i !== index);
localStorage.setItem("signingConditions", JSON.stringify(signingConditions));
};

View File

@ -0,0 +1,30 @@
import { PKPEthersWallet } from '@lit-protocol/pkp-ethers';
export async function sendTxWithPKPWallet(pkp, sessionSigs) {
const pkpWallet = new PKPEthersWallet({
controllerSessionSigs: sessionSigs,
pkpPubKey: pkp.publicKey,
rpc: "https://rpc.gnosischain.com/"
});
await pkpWallet.init();
const from = pkpWallet.address;
const to = '0x1A5cfC9EA11afb50011F847fb7dC07bA1e18b05A';
const value = BigInt(100000000000000000);
const gasLimit = 21000;
const tx = {
from,
to,
value,
gasLimit,
};
console.log('transaction created: ' + tx);
const signedTx = await pkpWallet.signTransaction(tx);
console.log('transaction signed: ' + signedTx);
await pkpWallet.sendTransaction(signedTx);
console.log('transaction sent');
}

View File

@ -20,7 +20,20 @@
export let data: LayoutData;
const token = Cookies.get("token");
const signingConditionsCookie = Cookies.get("signingConditions");
let signingConditions = signingConditionsCookie
? JSON.parse(signingConditionsCookie)
: [];
let correctCondition = signingConditions
? signingConditions.find(
(condition) =>
condition.resourceId.baseUrl === "https://localhost:3000" &&
condition.resourceId.path === "/server/wundergraph" &&
condition.resourceId.role === "owner"
)
: null;
const token = correctCondition ? correctCondition.jwt : null;
googleSession.subscribe((value) => {
activeSession = value.activeSession;
@ -31,7 +44,6 @@
});
if (token) {
console.log("layout jwt token: " + token);
client.setAuthorizationToken(token);
}
</script>
@ -43,26 +55,31 @@
<!-- (fallback contents) -->
{/if}</Drawer
>
<div
class="grid h-screen bg-center bg-cover grid-rows-layout"
style="background-image: url('lake.jpeg');"
>
<QueryClientProvider client={data.queryClient}>
<div class="w-full h-full p-6 overflow-hidden">
<div class="w-full h-full overflow-hidden bg-white rounded-xl">
<div class="grid h-screen grid-layout bg-color">
<QueryClientProvider client={data.queryClient} class="main">
<slot />
</div>
</div>
</QueryClientProvider>
<div class="row-start-2 row-end-3">
<footer>
<Wallet />
</div>
</footer>
</div>
<style>
.grid-rows-layout {
.bg-color {
background-color: #e6e7e1;
}
.grid-layout {
grid-template-areas:
"main "
"footer ";
grid-template-rows: 1fr auto;
}
.main {
grid-area: main;
}
footer {
grid-area: footer;
}
</style>

View File

@ -9,6 +9,8 @@
// export let data: PageData;
</script>
<div class="w-full h-full p-6 overflow-hidden">
<div class="w-full h-full overflow-hidden bg-white rounded-xl">
<section
class="flex flex-col items-center justify-center min-h-screen p-8 space-y-8 text-white bg-green-500"
>
@ -18,14 +20,18 @@
<h1 class="text-6xl font-bold text-center">Become a Vision Architect</h1>
<p class="text-xl text-center">
We are committed to creating an amazing life experience for every human on
the planet. Our mission is to foster a world where everyone can thrive in
abundance, excitement, and creativity. We envision a sustainable green
planet and future cities where innovation and nature coexist harmoniously.
We are committed to creating an amazing life experience for every human
on the planet. Our mission is to foster a world where everyone can
thrive in abundance, excitement, and creativity. We envision a
sustainable green planet and future cities where innovation and nature
coexist harmoniously.
</p>
<h3 class="text-2xl">Stand Up NOW, Break Free And Follow Your Passions</h3>
<h3 class="text-2xl">
Stand Up NOW, Break Free And Follow Your Passions
</h3>
</section>
</div>
</div>
<!-- <div class="bg-white">myPKP {data.myPKP}</div> -->
<!-- <button on:click={trigger}>Sign Request</button> -->

View File

@ -0,0 +1,34 @@
import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';
import { env } from '$env/dynamic/private';
// You may want to replace the above with a static private env variable
// for dead-code elimination and build-time type-checking:
// import { OPENAI_API_KEY } from '$env/static/private'
import type { RequestHandler } from './$types';
// Create an OpenAI API client
const openai = new OpenAI({
apiKey: env.OPENAI_API_KEY || '',
});
export const POST = (async ({ request }) => {
// Extract the `prompt` from the body of the request
const { messages } = await request.json();
// Ask OpenAI for a streaming chat completion given the prompt
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages: messages.map((message: any) => ({
content: message.content,
role: message.role,
})),
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
}) satisfies RequestHandler;

View File

@ -0,0 +1,95 @@
<script>
import Icon from "@iconify/svelte";
</script>
<div class="w-full h-full overflow-hidden grid grid-cols-6">
<aside class="col-span-1">
<nav class="list-nav p-2">
<h3 class="text-xl font-bold mb-4">MY HOME</h3>
<ul>
<li>
<a href="/me">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Dashboard
</a>
</li>
<li>
<a href="/me/projects">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
My Projects
</a>
</li>
<li>
<a href="/me/contacts">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Contacts
</a>
</li>
<li>
<a href="/me/conversations">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Email
</a>
</li>
<li>
<a href="/me/chatGPT">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
ChatGPT
</a>
</li>
<li>
<a href="/me/banking">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Banking
</a>
</li>
<li>
<a href="/me/paperless">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Paperless
</a>
</li>
<li>
<a href="/me/acc">
<Icon
icon="iconamoon:profile-circle-fill"
class="w-8 h-8 text-gray-500"
/>
Access Control
</a>
</li>
</ul>
</nav>
</aside>
<div class="col-span-5 w-full h-full overflow-hidden bg-white rounded-bl-3xl">
<slot />
</div>
</div>
<style>
.collapsed {
width: 3rem; /* Adjust as needed */
}
</style>

View File

@ -1,22 +1,48 @@
<script lang="ts">
import { createQuery } from "../../lib/wundergraph";
import JWT from "$lib/JWT.svelte";
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { Avatar } from "@skeletonlabs/skeleton";
const projectsQuery = createQuery({
operationName: "Projects",
import { createQuery } from "../../lib/wundergraph";
const meQuery = createQuery({
operationName: "Me",
});
</script>
<div class="w-full h-full overflow-y-auto">
<JWT />
<HeaderMain>
<div slot="header">
<h1>Profile</h1>
</div>
<div class="w-full h-full results">
{#if $projectsQuery.isLoading}
<div slot="main" class="h-full w-full overflow-scroll">
<div class="w-full h-full overflow-scroll">
{#if $meQuery.isLoading}
<p>Loading...</p>
{:else if $projectsQuery.error}
<pre>Error: {JSON.stringify($projectsQuery.error, null, 2)}</pre>
{:else if $meQuery.error}
<pre>Error: {JSON.stringify($meQuery.error, null, 2)}</pre>
{:else}
<pre>{JSON.stringify($projectsQuery.data, null, 2)}</pre>
<div class="container mx-auto p-8 space-y-8">
<div class="flex items-center space-x-4">
<Avatar
class="rounded-full w-24"
src={`https://directus.andert.me/assets/${
$meQuery.data.system_db_users_me?.avatar.id
}?access_token=${
import.meta.env.VITE_DIRECTUS_TEMPORARY_ACCESS_TOKEN
}`}
/>
<div>
<h3 class="h3">Welcome back</h3>
<h2 class="h2">
{$meQuery.data.system_db_users_me?.first_name}
{$meQuery.data.system_db_users_me?.last_name}
</h2>
{$meQuery.data.system_db_users_me?.external_identifier}
<p />
</div>
</div>
</div>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,14 @@
<script>
import ACCs from "$lib/ACCs.svelte";
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
</script>
<HeaderMain>
<div slot="header">
<h1>Access Control</h1>
</div>
<div slot="main">
<ACCs />
</div>
</HeaderMain>

View File

@ -0,0 +1,13 @@
<!-- src/routes/apps/+page.svelte -->
<script>
import { mockApps } from "$lib/mockApps.js";
</script>
<h1>Apps List</h1>
<ul>
{#each mockApps as app}
<li>
<a href="/me/apps/{app.name}">{app.name}</a>
</li>
{/each}
</ul>

View File

@ -0,0 +1,12 @@
import { mockApps } from '$lib/mockApps';
export function load({ params }) {
const { name } = params;
const app = mockApps.find(a => a.name == name);
if (app) {
return { props: { app } };
}
return { status: 404 };
}

View File

@ -0,0 +1,10 @@
<script>
/** @type {import('./$types').PageData} */
export let data;
</script>
<iframe
src={`https://${data.props.app.name}.andert.me`}
width="100%"
height="100%"
/>

View File

@ -0,0 +1,98 @@
<script lang="ts">
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { createQuery } from "$lib/wundergraph";
import Time from "svelte-time";
import Icon from "@iconify/svelte";
import { sendTxWithPKPWallet } from "$lib/services/sendTxWithPKPWallet";
let me = JSON.parse(localStorage.getItem("me"));
const getBalanceQuery = createQuery({
operationName: "getBalance",
input: {
address: me.pkps[0].ethAddress,
},
});
const getTransactionsQuery = createQuery({
operationName: "getTransactions",
input: {
address: me.pkps[0].ethAddress,
},
});
function fromWei(wei: BigInt): string {
return (Number(wei) / 10 ** 18).toFixed(2);
}
function handleSendTx() {
sendTxWithPKPWallet(me.pkps[0], me.sessionSigs);
}
</script>
<HeaderMain>
<div slot="header">
<h1>Banking</h1>
</div>
<div slot="main">
<div class="pb-4">
ACCOUNT
<p class="text-2xl">{me.pkps[0].ethAddress}</p>
</div>
<div class="pb-4">
{#if $getBalanceQuery.isLoading}
<p>Loading...</p>
{:else if $getBalanceQuery.error}
<pre>Error: {JSON.stringify($getBalanceQuery.error, null, 2)}</pre>
{:else}
BALANCE
<p class="text-5xl">${fromWei($getBalanceQuery.data.balance)}</p>
{/if}
</div>
<div class="pb-4">
<button
class="btn variant-filled-primary flex-grow"
on:click={handleSendTx}
>
SEND $0.10
</button>
</div>
<div class="pb-4">
{#if $getTransactionsQuery.isLoading}
<p>Loading...</p>
{:else if $getTransactionsQuery.error}
<pre>Error: {JSON.stringify($getTransactionsQuery.error, null, 2)}</pre>
{:else}
<div class="pb-4">TRANSACTIONS</div>
<ul class="list-disc">
{#each $getTransactionsQuery.data.transactions as transaction (transaction.hash)}
<li class="flex justify-between items-center mb-4">
<div class="flex items-center">
<div class="pr-3">
<Icon icon="ri:user-received-2-line" width="44" height="44" />
</div>
<div>
<p class="text-xl">
from {transaction.from.substring(0, 10)}...
</p>
<p class="text-sm text-gray-500">
{transaction.hash}
</p>
</div>
</div>
<div class="text-right">
<p class="text-2xl">
${fromWei(BigInt(transaction.value))}
</p>
<p class="text-sm text-gray-500">
<Time timestamp={transaction.timestamp} relative />
</p>
</div>
</li>
{/each}
</ul>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,40 @@
<script lang="ts">
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { createQuery } from "../../../lib/wundergraph";
const bookmarksQuery = createQuery({
operationName: "getBookmarks",
});
</script>
<HeaderMain>
<div slot="header">
<h1>Bookmarks</h1>
</div>
<div slot="main">
<div class="w-full h-full overflow-y-auto">
<div class="w-full h-full results">
{#if $bookmarksQuery.isLoading}
<p>Loading...</p>
{:else if $bookmarksQuery.error}
<pre>Error: {JSON.stringify($bookmarksQuery.error, null, 2)}</pre>
{:else}
{#each $bookmarksQuery.data.db_bookmarks as bookmark (bookmark.id)}
<a href={bookmark.url} class="text-blue-500 hover:underline">
{bookmark.name}
</a>
<div class="mt-2">
{#each bookmark.tags as tag (tag)}
<span
class="inline-block bg-blue-200 text-blue-800 px-2 py-1 rounded-full mr-2 mb-2"
>{tag}</span
>
{/each}
</div>
{/each}
{/if}
</div>
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,5 @@
<script>
import Ai from "$lib/Ai.svelte";
</script>
<Ai />

View File

@ -0,0 +1,36 @@
<script lang="ts">
import JsonViewer from "$lib/JsonViewer.svelte";
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { createQuery } from "../../../lib/wundergraph";
const contactsQuery = createQuery({
operationName: "getChatwootContacts",
});
</script>
<HeaderMain>
<div slot="header">
<h1>Contacts</h1>
</div>
<div slot="main" class="h-full w-full overflow-scroll">
<div class="w-full h-full overflow-scroll">
{#if $contactsQuery.isLoading}
<p>Loading...</p>
{:else if $contactsQuery.error}
<pre>Error: {JSON.stringify($contactsQuery.error, null, 2)}</pre>
{:else}
<div class="grid grid-cols-3 gap-4">
{#each $contactsQuery.data.payload as contact (contact.id)}
<div class="card">
<header class="card-header">{contact.name}</header>
<section class="p-4">
ID: {contact.id}<br />{contact.email}
</section>
</div>
{/each}
</div>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,92 @@
<script lang="ts">
import MailViewer from "$lib/MailViewer.svelte";
import { createQuery } from "../../../lib/wundergraph";
import JsonViewer from "$lib/JsonViewer.svelte";
const conversationsQuery = createQuery({
operationName: "getChatwootConversations",
});
let selectedConversation = null;
let messagesQuery;
function selectConversation(conversation) {
selectedConversation = conversation;
}
$: if ($conversationsQuery.data && !selectedConversation) {
selectedConversation = $conversationsQuery.data.data.payload[0];
}
$: if (selectedConversation) {
messagesQuery = createQuery({
operationName: "getChatwootMessages",
input: { conversationId: selectedConversation.id },
});
}
</script>
<div class="h-full w-full overflow-scroll flex">
<div class="w-1/3 h-full overflow-scroll">
{#if $conversationsQuery.isLoading}
<p>Loading...</p>
{:else if $conversationsQuery.error}
Error: <JsonViewer json="$conversationsQuery.error}" />
{:else}
{#each $conversationsQuery.data.data.payload as conversation (conversation.id)}
<div
class="m-1 p-2 border border-gray-200 rounded-md hover:bg-gray-100 cursor-pointer"
on:click={() => selectConversation(conversation)}
>
<h2 class="text-lg font-semibold">
{conversation.meta.sender.name}
</h2>
<p>{conversation.messages[0].content.slice(0, 100)}...</p>
</div>
{/each}
{/if}
</div>
<div class="w-2/3 h-full overflow-scroll relative">
{#if selectedConversation}
<div class="p-4 bg-white z-10 sticky top-0 left-0">
<h2 class="font-bold text-xl">
{selectedConversation.meta.sender.name}
</h2>
<p>
{selectedConversation.id} - {selectedConversation.meta.sender.email}
</p>
</div>
<div class="space-y-4 px-4">
{#if $messagesQuery && $messagesQuery.data}
{#each $messagesQuery.data.payload as message (message.id)}
{#if message.content_attributes.email && (message.content_attributes.email.content_type.includes("text/html") || message.content_attributes.email.content_type.includes("multipart/alternative"))}
<MailViewer
html={message.content_attributes.email.html_content.full}
/>
{:else}
<div
class="p-4 max-w-xs mx-auto bg-blue-100 rounded-xl shadow-md flex items-center space-x-4"
>
<p class="text-black">{message.content}</p>
</div>
{/if}
{/each}
{/if}
</div>
{:else}
<p>Select a conversation to view its details.</p>
{/if}
</div>
</div>
<style>
.btn {
display: inline-block;
padding: 0.5em 1em;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 0.25em;
}
</style>

View File

@ -0,0 +1,57 @@
<script lang="ts">
import { createQuery } from "../../../lib/wundergraph";
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
const filesQuery = createQuery({
operationName: "Files",
});
</script>
<HeaderMain>
<div slot="header">
<h1>Files</h1>
</div>
<div slot="main" class="h-full w-full overflow-scroll">
<div class="w-full h-full overflow-scroll">
{#if $filesQuery.isLoading}
<p>Loading...</p>
{:else if $filesQuery.error}
<pre>Error: {JSON.stringify($filesQuery.error, null, 2)}</pre>
{:else}
<div class="space-y-4">
{#each $filesQuery.data.system_db_files as file}
<div class="flex">
<div class="w-1/4">
{#if file.type === "application/pdf"}
<object
data={`https://directus.andert.me/assets/${
file.id
}?access_token=${
import.meta.env.VITE_DIRECTUS_TEMPORARY_ACCESS_TOKEN
}`}
type="application/pdf"
width="100%"
height="200px"
/>
{:else}
<img
src={`https://directus.andert.me/assets/${
file.id
}?access_token=${
import.meta.env.VITE_DIRECTUS_TEMPORARY_ACCESS_TOKEN
}`}
width="200"
/>
{/if}
</div>
<div class="w-3/4 p-4">
<h2>{file.title}</h2>
<p>{file.id}</p>
</div>
</div>
{/each}
</div>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,67 @@
<script lang="ts">
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { createQuery } from "../../../lib/wundergraph";
const paperlessQuery = createQuery({
operationName: "getPaperless",
});
let pdfDataUrls = [];
let selectedPdfUrl = null;
$: if ($paperlessQuery.data) {
pdfDataUrls = $paperlessQuery.data.map((document) => {
const pdfData = atob(document.pdfData);
const pdfDataArray = new Uint8Array(pdfData.length);
for (let i = 0; i < pdfData.length; i++) {
pdfDataArray[i] = pdfData.charCodeAt(i);
}
const blob = new Blob([pdfDataArray], { type: "application/pdf" });
return URL.createObjectURL(blob);
});
}
function selectPdf(i) {
selectedPdfUrl = pdfDataUrls[i];
}
</script>
<HeaderMain>
<div slot="main" class="h-full w-full overflow-scroll flex">
<div class="w-1/4 h-full overflow-scroll">
{#if $paperlessQuery.isLoading}
<p>Loading...</p>
{:else if $paperlessQuery.error}
<pre>Error: {JSON.stringify($paperlessQuery.error, null, 2)}</pre>
{:else}
{#each $paperlessQuery.data as document, i}
<a href="#" on:click|preventDefault={() => selectPdf(i)}>
<div
class="my-4 p-2 border border-gray-200 rounded-md hover:bg-gray-100 cursor-pointer"
>
<h2 class="text-lg font-semibold">
{document.archived_file_name}
</h2>
<p>Correspondent: {document.correspondent?.name || "N/A"}</p>
<p>
Tags: {document.tags?.map((tag) => tag.name).join(", ") ||
"N/A"}
</p>
<p>{document.documentType?.name || "N/A"}</p>
</div>
</a>
{/each}
{/if}
</div>
<div class="w-3/4 h-full overflow-scroll">
{#if selectedPdfUrl}
<object
data={selectedPdfUrl}
type="application/pdf"
width="100%"
height="100%"
/>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,26 @@
<script lang="ts">
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
import { createQuery } from "../../../lib/wundergraph";
const projectsQuery = createQuery({
operationName: "Projects",
});
</script>
<HeaderMain>
<div slot="header">
<h1>Projects</h1>
</div>
<div slot="main" class="h-full w-full overflow-scroll">
<div class="w-full h-full overflow-scroll">
{#if $projectsQuery.isLoading}
<p>Loading...</p>
{:else if $projectsQuery.error}
<pre>Error: {JSON.stringify($projectsQuery.error, null, 2)}</pre>
{:else}
<pre>{JSON.stringify($projectsQuery.data, null, 2)}</pre>
{/if}
</div>
</div>
</HeaderMain>

View File

@ -0,0 +1,34 @@
import OpenAI from 'openai';
import { OpenAIStream, StreamingTextResponse } from 'ai';
import { env } from '$env/dynamic/private';
// You may want to replace the above with a static private env variable
// for dead-code elimination and build-time type-checking:
// import { OPENAI_API_KEY } from '$env/static/private'
import type { RequestHandler } from '../../server/api/chat/$types';
// Create an OpenAI API client
const openai = new OpenAI({
apiKey: env.OPENAI_API_KEY || '',
});
export const POST = (async ({ request }) => {
// Extract the `prompt` from the body of the request
const { messages } = await request.json();
// Ask OpenAI for a streaming chat completion given the prompt
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
stream: true,
messages: messages.map((message: any) => ({
content: message.content,
role: message.role,
})),
});
// Convert the response into a friendly text-stream
const stream = OpenAIStream(response);
// Respond with the stream
return new StreamingTextResponse(stream);
}) satisfies RequestHandler;

View File

@ -13,21 +13,22 @@ export async function GET({ request }) {
}
try {
const { payload } = await verifyJwt({ jwt: token });
const { verified, payload } = await verifyJwt({ jwt: token });
if (
payload.baseUrl !== "https://localhost:3000/" ||
payload.path !== "wunderauth" ||
payload.baseUrl !== "https://localhost:3000" ||
payload.path !== "/server/wundergraph" ||
payload.orgId !== "°" ||
payload.role !== "owner" ||
payload.extraData !== ""
) {
console.log("JWT payload not matching");
throw error(401, "JWT payload not macting")
throw error(401, "JWT payload not matching")
}
console.log(payload);
console.log("JWT Server request verified: ", verified);
console.log("JWT Server request payload: ", payload);
return new Response(JSON.stringify(payload), { status: 200 });
} catch (err) {
console.log("JWT error");
throw error(401, "JWT payload not machting")
throw error(401, "JWT error")
}
}

View File

@ -1,6 +1,7 @@
import { join } from 'path';
import type { Config } from 'tailwindcss';
import forms from '@tailwindcss/forms';
// 1. Import the Skeleton plugin
import { skeleton } from '@skeletonlabs/tw-plugin';
@ -20,6 +21,7 @@ const config = {
extend: {},
},
plugins: [
forms,
// 4. Append the Skeleton plugin (after other plugins)
skeleton({
themes: { preset: ["wintry"] }

View File

@ -1,7 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { fetchSchemas } from './.wundergraph/schemas/fetch-schemas';
import { vitePreprocess } from '@sveltejs/kit/vite';
export default defineConfig({
plugins: [

8978
yarn.lock Normal file

File diff suppressed because it is too large Load Diff