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 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}
<button on:click={createNewACC}>Create New ACC</button>
{/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,30 +75,40 @@
</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="flex items-center space-x-2">
<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="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>
<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
>
</div>
</div>
</div>
<button
on:click={signRequestTrigger}
type="button"
class="btn variant-filled">SignRequest</button
>
<button type="button" class="btn variant-filled" on:click={clearSession}
>Logout</button
>
</div>
</div>
{:else if $state.matches("sessionExpired")}
@ -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({
pkps: (_, event) => event.data,
}),
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">
<slot />
</div>
</div>
<div class="grid h-screen grid-layout bg-color">
<QueryClientProvider client={data.queryClient} class="main">
<slot />
</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,23 +9,29 @@
// export let data: PageData;
</script>
<section
class="flex flex-col items-center justify-center min-h-screen p-8 space-y-8 text-white bg-green-500"
>
<h2 class="text-4xl font-bold text-center">
Unleash Your Full Potential and Transform Your Life <br />
</h2>
<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.
</p>
<h3 class="text-2xl">Stand Up NOW, Break Free And Follow Your Passions</h3>
</section>
<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"
>
<h2 class="text-4xl font-bold text-center">
Unleash Your Full Potential and Transform Your Life <br />
</h2>
<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.
</p>
<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 />
<div class="w-full h-full results">
{#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}
<HeaderMain>
<div slot="header">
<h1>Profile</h1>
</div>
</div>
<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 $meQuery.error}
<pre>Error: {JSON.stringify($meQuery.error, null, 2)}</pre>
{:else}
<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