Compare commits
30 Commits
95566d6f36
...
main
Author | SHA1 | Date | |
---|---|---|---|
a7eed530a3 | |||
af15eb1d0b | |||
19674310e7 | |||
5e3631c49d | |||
0312f50d51 | |||
804a618053 | |||
5041a3c5a3 | |||
91b351ef2e | |||
b1185b44dd | |||
fd534bfae7 | |||
d55e4bb203 | |||
563e9945c3 | |||
aaf4a7461a | |||
102f4fa855 | |||
35b3167509 | |||
53e68aa362 | |||
8a4ec6cc73 | |||
4ae46a2a75 | |||
c13047e281 | |||
61ba4d0e4f | |||
9b9ac7d89e | |||
293180c2b5 | |||
24b83ec99e | |||
879f7aad3f | |||
efc2b41f9e | |||
7652938c74 | |||
b8012cf9fd | |||
978e438854 | |||
9aa3bfc0d2 | |||
042f9209ed |
@ -1,6 +0,0 @@
|
|||||||
query Continents {
|
|
||||||
countries_continents {
|
|
||||||
name
|
|
||||||
code
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
query Countries($filter: countries_CountryFilterInput) {
|
|
||||||
countries_countries(filter: $filter) {
|
|
||||||
code
|
|
||||||
name
|
|
||||||
capital
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
query Dragons {
|
|
||||||
spacex_dragons {
|
|
||||||
name
|
|
||||||
active
|
|
||||||
}
|
|
||||||
}
|
|
8
.wundergraph/operations/Files.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
query {
|
||||||
|
system_db_files {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
type
|
||||||
|
filename_disk
|
||||||
|
}
|
||||||
|
}
|
11
.wundergraph/operations/Me.graphql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
query {
|
||||||
|
system_db_users_me {
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
avatar {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
external_identifier
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
query Projects {
|
query {
|
||||||
directus_projects {
|
db_projects {
|
||||||
id
|
id
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
query Todos {
|
query {
|
||||||
directus_todos {
|
db_todos {
|
||||||
id
|
id
|
||||||
task
|
task
|
||||||
user_created {
|
user_created {
|
||||||
|
27
.wundergraph/operations/getBalance.ts
Normal 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),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
8
.wundergraph/operations/getBookmarks.graphql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
query {
|
||||||
|
db_bookmarks {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
url
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
}
|
20
.wundergraph/operations/getChatwootContacts.ts
Normal 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;
|
||||||
|
},
|
||||||
|
});
|
19
.wundergraph/operations/getChatwootConversations.ts
Normal 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;
|
||||||
|
},
|
||||||
|
});
|
20
.wundergraph/operations/getChatwootMessages.ts
Normal 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;
|
||||||
|
},
|
||||||
|
});
|
70
.wundergraph/operations/getPaperless.ts
Normal 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;
|
||||||
|
},
|
||||||
|
});
|
27
.wundergraph/operations/getTransactions.ts
Normal 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(),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
5195
.wundergraph/schemas/cloudron.json
Normal file
@ -2,6 +2,9 @@ type Query {
|
|||||||
projects(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [projects!]!
|
projects(filter: projects_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [projects!]!
|
||||||
projects_by_id(id: ID!): 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!]!
|
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(filter: todos_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): [todos!]!
|
||||||
todos_by_id(id: ID!): 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!]!
|
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 {
|
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_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_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_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
|
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_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_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_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_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_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
|
update_todos_item(id: ID!, data: update_todos_input!): todos
|
||||||
delete_projects_items(ids: [ID]!): delete_many
|
delete_projects_items(ids: [ID]!): delete_many
|
||||||
delete_projects_item(id: ID!): delete_one
|
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_items(ids: [ID]!): delete_many
|
||||||
delete_todos_item(id: ID!): delete_one
|
delete_todos_item(id: ID!): delete_one
|
||||||
}
|
}
|
||||||
|
|
||||||
type Subscription {
|
type Subscription {
|
||||||
projects_mutated(event: EventEnum): projects_mutated
|
projects_mutated(event: EventEnum): projects_mutated
|
||||||
todos_mutated(event: EventEnum): todos_mutated
|
|
||||||
directus_dashboards_mutated(event: EventEnum): directus_dashboards_mutated
|
directus_dashboards_mutated(event: EventEnum): directus_dashboards_mutated
|
||||||
directus_activity_mutated(event: EventEnum): directus_activity_mutated
|
directus_activity_mutated(event: EventEnum): directus_activity_mutated
|
||||||
directus_notifications_mutated(event: EventEnum): directus_notifications_mutated
|
directus_notifications_mutated(event: EventEnum): directus_notifications_mutated
|
||||||
@ -44,6 +53,8 @@ type Subscription {
|
|||||||
directus_users_mutated(event: EventEnum): directus_users_mutated
|
directus_users_mutated(event: EventEnum): directus_users_mutated
|
||||||
directus_shares_mutated(event: EventEnum): directus_shares_mutated
|
directus_shares_mutated(event: EventEnum): directus_shares_mutated
|
||||||
directus_webhooks_mutated(event: EventEnum): directus_webhooks_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`."""
|
"""The `Boolean` scalar type represents `true` or `false`."""
|
||||||
@ -92,6 +103,58 @@ enum EventEnum {
|
|||||||
delete
|
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 {
|
type count_functions {
|
||||||
count: Int
|
count: Int
|
||||||
}
|
}
|
||||||
@ -557,15 +620,16 @@ type projects_mutated {
|
|||||||
|
|
||||||
type todos {
|
type todos {
|
||||||
id: ID!
|
id: ID!
|
||||||
status: String
|
|
||||||
sort: Int
|
|
||||||
user_created(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
|
user_created(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
|
||||||
date_created: Date
|
date_created: Date
|
||||||
date_created_func: datetime_functions
|
date_created_func: datetime_functions
|
||||||
user_updated(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
|
user_updated(filter: directus_users_filter, sort: [String], limit: Int, offset: Int, page: Int, search: String): directus_users
|
||||||
date_updated: Date
|
date_updated: Date
|
||||||
date_updated_func: datetime_functions
|
date_updated_func: datetime_functions
|
||||||
|
enddate: Date
|
||||||
|
enddate_func: datetime_functions
|
||||||
task: String
|
task: String
|
||||||
|
type: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type todos_aggregated {
|
type todos_aggregated {
|
||||||
@ -573,27 +637,17 @@ type todos_aggregated {
|
|||||||
countAll: Int
|
countAll: Int
|
||||||
count: todos_aggregated_count
|
count: todos_aggregated_count
|
||||||
countDistinct: 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 {
|
type todos_aggregated_count {
|
||||||
id: Int
|
id: Int
|
||||||
status: Int
|
|
||||||
sort: Int
|
|
||||||
user_created: Int
|
user_created: Int
|
||||||
date_created: Int
|
date_created: Int
|
||||||
user_updated: Int
|
user_updated: Int
|
||||||
date_updated: Int
|
date_updated: Int
|
||||||
|
enddate: Int
|
||||||
task: Int
|
task: Int
|
||||||
}
|
type: Int
|
||||||
|
|
||||||
type todos_aggregated_fields {
|
|
||||||
sort: Float
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type todos_mutated {
|
type todos_mutated {
|
||||||
@ -602,6 +656,24 @@ type todos_mutated {
|
|||||||
data: todos
|
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 {
|
input boolean_filter_operators {
|
||||||
_eq: Boolean
|
_eq: Boolean
|
||||||
_neq: Boolean
|
_neq: Boolean
|
||||||
@ -613,6 +685,19 @@ input count_function_filter_operators {
|
|||||||
count: number_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 {
|
input create_directus_files_input {
|
||||||
id: ID
|
id: ID
|
||||||
storage: String!
|
storage: String!
|
||||||
@ -692,13 +777,13 @@ input create_projects_input {
|
|||||||
|
|
||||||
input create_todos_input {
|
input create_todos_input {
|
||||||
id: ID
|
id: ID
|
||||||
status: String
|
|
||||||
sort: Int
|
|
||||||
user_created: create_directus_users_input
|
user_created: create_directus_users_input
|
||||||
date_created: Date
|
date_created: Date
|
||||||
user_updated: create_directus_users_input
|
user_updated: create_directus_users_input
|
||||||
date_updated: Date
|
date_updated: Date
|
||||||
|
enddate: Date
|
||||||
task: String
|
task: String
|
||||||
|
type: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input date_filter_operators {
|
input date_filter_operators {
|
||||||
@ -979,19 +1064,33 @@ input string_filter_operators {
|
|||||||
|
|
||||||
input todos_filter {
|
input todos_filter {
|
||||||
id: string_filter_operators
|
id: string_filter_operators
|
||||||
status: string_filter_operators
|
|
||||||
sort: number_filter_operators
|
|
||||||
user_created: directus_users_filter
|
user_created: directus_users_filter
|
||||||
date_created: date_filter_operators
|
date_created: date_filter_operators
|
||||||
date_created_func: datetime_function_filter_operators
|
date_created_func: datetime_function_filter_operators
|
||||||
user_updated: directus_users_filter
|
user_updated: directus_users_filter
|
||||||
date_updated: date_filter_operators
|
date_updated: date_filter_operators
|
||||||
date_updated_func: datetime_function_filter_operators
|
date_updated_func: datetime_function_filter_operators
|
||||||
|
enddate: date_filter_operators
|
||||||
|
enddate_func: datetime_function_filter_operators
|
||||||
task: string_filter_operators
|
task: string_filter_operators
|
||||||
|
type: string_filter_operators
|
||||||
_and: [todos_filter]
|
_and: [todos_filter]
|
||||||
_or: [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 {
|
input update_directus_files_input {
|
||||||
id: ID
|
id: ID
|
||||||
storage: String
|
storage: String
|
||||||
@ -1071,11 +1170,11 @@ input update_projects_input {
|
|||||||
|
|
||||||
input update_todos_input {
|
input update_todos_input {
|
||||||
id: ID
|
id: ID
|
||||||
status: String
|
|
||||||
sort: Int
|
|
||||||
user_created: update_directus_users_input
|
user_created: update_directus_users_input
|
||||||
date_created: Date
|
date_created: Date
|
||||||
user_updated: update_directus_users_input
|
user_updated: update_directus_users_input
|
||||||
date_updated: Date
|
date_updated: Date
|
||||||
|
enddate: Date
|
||||||
task: String
|
task: String
|
||||||
|
type: String
|
||||||
}
|
}
|
2366
.wundergraph/schemas/directus_system.graphql
Normal 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
|
// Save the schema to a file
|
||||||
fs.writeFileSync('./.wundergraph/schemas/directus.graphql', schema);
|
fs.writeFileSync('./.wundergraph/schemas/directus.graphql', schema);
|
||||||
|
fs.writeFileSync('./.wundergraph/schemas/directus_system.graphql', systemSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSchemas().catch(console.error);
|
fetchSchemas().catch(console.error);
|
181
.wundergraph/schemas/placeholder.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,28 +7,36 @@ import dotenv from 'dotenv';
|
|||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const directusSchema = fs.readFileSync(path.join(path.resolve(), './schemas/directus.graphql'), 'utf8');
|
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({
|
const db = introspect.graphql({
|
||||||
apiNamespace: 'countries',
|
apiNamespace: 'db',
|
||||||
url: 'https://countries.trevorblades.com/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const spaceX = introspect.graphql({
|
|
||||||
apiNamespace: 'spacex',
|
|
||||||
url: 'https://spacex-api.fly.dev/graphql/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const directus = introspect.graphql({
|
|
||||||
apiNamespace: 'directus',
|
|
||||||
loadSchemaFromString: directusSchema,
|
loadSchemaFromString: directusSchema,
|
||||||
url: 'https://directus.andert.me/graphql',
|
url: 'https://directus.andert.me/graphql',
|
||||||
headers: (builder) => builder
|
headers: (builder) => builder
|
||||||
.addStaticHeader('Authorization', new EnvironmentVariable('DIRECTUS', process.env.DIRECTUS))
|
.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
|
// configureWunderGraph emits the configuration
|
||||||
configureWunderGraphApplication({
|
configureWunderGraphApplication({
|
||||||
apis: [countries, spaceX, directus],
|
apis: [db, system_db, placeholder],
|
||||||
server,
|
server,
|
||||||
operations,
|
operations,
|
||||||
generate: {
|
generate: {
|
||||||
@ -59,12 +67,12 @@ configureWunderGraphApplication({
|
|||||||
tokenBased: {
|
tokenBased: {
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
userInfoEndpoint: 'http://localhost:3000/api/auth/session',
|
userInfoEndpoint: 'http://localhost:3000/server/wundergraph',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authorization: {
|
authorization: {
|
||||||
roles: ['admin'],
|
roles: ['owner'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
24
package.json
@ -16,32 +16,49 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fontsource/fira-mono": "^4.5.10",
|
"@fontsource/fira-mono": "^4.5.10",
|
||||||
"@neoconfetti/svelte": "^1.0.0",
|
"@neoconfetti/svelte": "^1.0.0",
|
||||||
|
"@skeletonlabs/skeleton": "^2.0.0",
|
||||||
|
"@skeletonlabs/tw-plugin": "^0.1.0",
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
|
"@sveltejs/adapter-static": "^2.0.3",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
|
"@tailwindcss/forms": "^0.5.6",
|
||||||
|
"@tauri-apps/cli": "2.0.0-alpha.14",
|
||||||
"@types/cookie": "^0.5.1",
|
"@types/cookie": "^0.5.1",
|
||||||
"@types/js-cookie": "^3.0.3",
|
"@types/js-cookie": "^3.0.3",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
|
"@types/node": "^20.5.8",
|
||||||
|
"autoprefixer": "^10.4.15",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
|
"dayjs": "^1.11.9",
|
||||||
|
"postcss": "^8.4.29",
|
||||||
"svelte": "^3.54.0",
|
"svelte": "^3.54.0",
|
||||||
"svelte-check": "^3.0.1",
|
"svelte-check": "^3.0.1",
|
||||||
|
"svelte-time": "^0.8.0",
|
||||||
|
"tailwindcss": "^3.3.3",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^4.2.0",
|
"vite": "^4.2.0",
|
||||||
"wait-on": "^7.0.1"
|
"wait-on": "^7.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@floating-ui/dom": "^1.5.1",
|
||||||
"@graphql-tools/graphql-file-loader": "^8.0.0",
|
"@graphql-tools/graphql-file-loader": "^8.0.0",
|
||||||
"@graphql-tools/load": "^8.0.0",
|
"@graphql-tools/load": "^8.0.0",
|
||||||
|
"@iconify/icons-ion": "^1.2.10",
|
||||||
"@iconify/svelte": "^3.1.4",
|
"@iconify/svelte": "^3.1.4",
|
||||||
"@lit-protocol/auth-helpers": "^2.2.50",
|
"@lit-protocol/auth-helpers": "^2.2.50",
|
||||||
"@lit-protocol/constants": "^2.2.50",
|
"@lit-protocol/constants": "^2.2.50",
|
||||||
"@lit-protocol/lit-auth-client": "^2.2.50",
|
"@lit-protocol/lit-auth-client": "^2.2.50",
|
||||||
"@lit-protocol/lit-node-client": "^2.2.50",
|
"@lit-protocol/lit-node-client": "^2.2.50",
|
||||||
"@lit-protocol/pkp-client": "^2.2.50",
|
"@lit-protocol/pkp-client": "^2.2.50",
|
||||||
|
"@lit-protocol/pkp-ethers": "^2.2.54",
|
||||||
"@lit-protocol/types": "^2.2.50",
|
"@lit-protocol/types": "^2.2.50",
|
||||||
"@tanstack/svelte-query": "^4.29.1",
|
"@tanstack/svelte-query": "^4.29.1",
|
||||||
|
"@wagmi/core": "^1.3.9",
|
||||||
"@wundergraph/sdk": "^0.174.5",
|
"@wundergraph/sdk": "^0.174.5",
|
||||||
"@wundergraph/svelte-query": "^0.3.10",
|
"@wundergraph/svelte-query": "^0.3.10",
|
||||||
|
"@xstate/svelte": "^2.1.0",
|
||||||
|
"ai": "^2.2.13",
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
@ -51,8 +68,13 @@
|
|||||||
"jsonwebtoken": "^9.0.1",
|
"jsonwebtoken": "^9.0.1",
|
||||||
"jwks-rsa": "^3.0.1",
|
"jwks-rsa": "^3.0.1",
|
||||||
"node-jose": "^2.2.0",
|
"node-jose": "^2.2.0",
|
||||||
|
"openai": "^4.8.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"url": "^0.11.1"
|
"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"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
11203
pnpm-lock.yaml
generated
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
3
src-tauri/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
3510
src-tauri/Cargo.lock
generated
Normal file
26
src-tauri/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "app"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
license = ""
|
||||||
|
repository = ""
|
||||||
|
default-run = "app"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.60"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "1.4.0", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
tauri = { version = "1.4.0", features = [] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
|
||||||
|
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
|
||||||
|
# DO NOT REMOVE!!
|
||||||
|
custom-protocol = [ "tauri/custom-protocol" ]
|
3
src-tauri/build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
BIN
src-tauri/icons/128x128.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
src-tauri/icons/icon.png
Normal file
After Width: | Height: | Size: 49 KiB |
8
src-tauri/src/main.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
66
src-tauri/tauri.conf.json
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
|
"build": {
|
||||||
|
"beforeBuildCommand": "pnpm run build",
|
||||||
|
"beforeDevCommand": "pnpm run dev",
|
||||||
|
"devPath": "http://localhost:3001",
|
||||||
|
"distDir": "../build"
|
||||||
|
},
|
||||||
|
"package": {
|
||||||
|
"productName": "°",
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"tauri": {
|
||||||
|
"allowlist": {
|
||||||
|
"all": false
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"category": "DeveloperTool",
|
||||||
|
"copyright": "",
|
||||||
|
"deb": {
|
||||||
|
"depends": []
|
||||||
|
},
|
||||||
|
"externalBin": [],
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
],
|
||||||
|
"identifier": "com.tauri.dev",
|
||||||
|
"longDescription": "",
|
||||||
|
"macOS": {
|
||||||
|
"entitlements": null,
|
||||||
|
"exceptionDomain": "",
|
||||||
|
"frameworks": [],
|
||||||
|
"providerShortName": null,
|
||||||
|
"signingIdentity": null
|
||||||
|
},
|
||||||
|
"resources": [],
|
||||||
|
"shortDescription": "",
|
||||||
|
"targets": "all",
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": null,
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"fullscreen": false,
|
||||||
|
"height": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"title": "wundergraph-sveltekit°",
|
||||||
|
"width": 800
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
3
src/app.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
17
src/app.d.ts
vendored
@ -1,12 +1,23 @@
|
|||||||
|
import type { Session } from 'svelte-kit-cookie-session';
|
||||||
|
|
||||||
|
type SessionData = {
|
||||||
|
views: number;
|
||||||
|
};
|
||||||
|
|
||||||
// See https://kit.svelte.dev/docs/types#app
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
// interface Error {}
|
// interface Error {}
|
||||||
// interface Locals {}
|
interface Locals {
|
||||||
// interface PageData {}
|
session: Session<SessionData>;
|
||||||
|
}
|
||||||
|
interface PageData {
|
||||||
|
// can add any properties here, return it from your root layout
|
||||||
|
session: SessionData;
|
||||||
|
}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export { };
|
@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-theme="wintry" data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
5
src/hooks.server.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { handleSession } from 'svelte-kit-cookie-session';
|
||||||
|
|
||||||
|
export const handle = handleSession({
|
||||||
|
secret: 'SOME_COMPLEX_SECRET_32_CHARSLONG'
|
||||||
|
});
|
74
src/lib/ACCs.svelte
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import {
|
||||||
|
createACC,
|
||||||
|
deleteACC,
|
||||||
|
} from "./services/mutateAccessControlConditions.ts";
|
||||||
|
let signingConditions =
|
||||||
|
JSON.parse(localStorage.getItem("signingConditions")) || [];
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
{#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>
|
49
src/lib/Ai.svelte
Normal 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>
|
@ -1,179 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import {
|
|
||||||
isSignInRedirect,
|
|
||||||
getProviderFromUrl,
|
|
||||||
} from "@lit-protocol/lit-auth-client";
|
|
||||||
import type { IRelayPKP } from "@lit-protocol/types";
|
|
||||||
import Icon from "@iconify/svelte";
|
|
||||||
import { createLitSession } from "./createLitSession";
|
|
||||||
import { connectProvider } from "./setupLit";
|
|
||||||
import Signer from "./Signer.svelte";
|
|
||||||
|
|
||||||
const redirectUri = "http://localhost:3000/";
|
|
||||||
|
|
||||||
let sessionSigs = null;
|
|
||||||
let error, currentPKP, authMethod, provider;
|
|
||||||
let status = "Initializing...";
|
|
||||||
let pkps: IRelayPKP[] = [];
|
|
||||||
let view = "SIGN_IN";
|
|
||||||
let sessionStatuses;
|
|
||||||
let activeSession = null;
|
|
||||||
|
|
||||||
let messageToSign = { user: "Sam", loggedIn: true };
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
// Load activeSession from local storage
|
|
||||||
const storedSession = localStorage.getItem("google-session");
|
|
||||||
const storedPKP = localStorage.getItem("current-pkp");
|
|
||||||
if (storedSession && storedPKP) {
|
|
||||||
activeSession = JSON.parse(storedSession);
|
|
||||||
currentPKP = JSON.parse(storedPKP);
|
|
||||||
view = "READY";
|
|
||||||
}
|
|
||||||
initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
$: if (sessionSigs) {
|
|
||||||
// Store sessionSigs in local storage in its original format
|
|
||||||
localStorage.setItem("google-session", JSON.stringify(sessionSigs));
|
|
||||||
|
|
||||||
// Update sessionStatuses
|
|
||||||
sessionStatuses = Object.entries(sessionSigs).map(([node, data]) => {
|
|
||||||
const sessionKey = JSON.parse(data.signedMessage).sessionKey;
|
|
||||||
const expiration = new Date(JSON.parse(data.signedMessage).expiration);
|
|
||||||
const isExpired = expiration < new Date();
|
|
||||||
return {
|
|
||||||
node,
|
|
||||||
sessionKey,
|
|
||||||
expiration: expiration.toISOString(),
|
|
||||||
isExpired,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Find an active session
|
|
||||||
activeSession = sessionStatuses.find(({ isExpired }) => !isExpired);
|
|
||||||
|
|
||||||
view = "READY";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initialize() {
|
|
||||||
status = "Connecting to Google provider...";
|
|
||||||
try {
|
|
||||||
provider = await connectProvider();
|
|
||||||
status = "Connected to Google provider.";
|
|
||||||
if (isSignInRedirect(redirectUri)) {
|
|
||||||
const providerName = getProviderFromUrl();
|
|
||||||
if (providerName) {
|
|
||||||
await handleRedirect(providerName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function authWithGoogle() {
|
|
||||||
try {
|
|
||||||
if (!provider) {
|
|
||||||
provider = await connectProvider();
|
|
||||||
status = "Reconnected to Google provider.";
|
|
||||||
}
|
|
||||||
await provider.signIn();
|
|
||||||
status = "Signing in with Google...";
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleRedirect(providerName: string) {
|
|
||||||
try {
|
|
||||||
if (!provider) throw new Error("Invalid provider.");
|
|
||||||
authMethod = await provider.authenticate();
|
|
||||||
status = "Authenticated successfully.";
|
|
||||||
pkps = await provider.fetchPKPsThroughRelayer(authMethod);
|
|
||||||
status = "Fetching your Google PKP...";
|
|
||||||
if (pkps.length === 0) {
|
|
||||||
status = "No PKPs found. Minting new PKP...";
|
|
||||||
await mint();
|
|
||||||
} else {
|
|
||||||
// Use the first PKP directly
|
|
||||||
await createSession(pkps[0]);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mint() {
|
|
||||||
const newPKP: IRelayPKP = await mintPkp(provider, authMethod);
|
|
||||||
pkps = [...pkps, newPKP];
|
|
||||||
status = "New PKP minted.";
|
|
||||||
await createSession(newPKP);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createSession(pkp: IRelayPKP) {
|
|
||||||
try {
|
|
||||||
currentPKP = pkp; // Assign the selected PKP to currentPKP
|
|
||||||
createLitSession(provider, pkp.publicKey, authMethod).then((sigs) => {
|
|
||||||
sessionSigs = sigs;
|
|
||||||
// Store sessionSigs and currentPKP in localStorage
|
|
||||||
localStorage.setItem("google-signature", JSON.stringify(sessionSigs));
|
|
||||||
localStorage.setItem("current-pkp", JSON.stringify(currentPKP));
|
|
||||||
});
|
|
||||||
status = "Session created successfully.";
|
|
||||||
} catch (err) {
|
|
||||||
setError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setError(err) {
|
|
||||||
error = err;
|
|
||||||
view = "ERROR";
|
|
||||||
status = `Error: ${err.message}`;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex items-center justify-center h-screen">
|
|
||||||
<div>
|
|
||||||
{#if error}
|
|
||||||
<div class="mt-4 text-center">
|
|
||||||
<h1>Error</h1>
|
|
||||||
<p>{error.message}</p>
|
|
||||||
</div>
|
|
||||||
{:else if view === "SIGN_IN"}
|
|
||||||
<button on:click={authWithGoogle} class="btn variant-filled">
|
|
||||||
<span><Icon icon="flat-color-icons:google" /></span>
|
|
||||||
<span>Sign in with Google</span>
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
{#if view === "READY"}
|
|
||||||
<div>
|
|
||||||
<h3>Your PKP Address:</h3>
|
|
||||||
<p>{currentPKP.ethAddress}</p>
|
|
||||||
</div>
|
|
||||||
Signer
|
|
||||||
<Signer {messageToSign} />
|
|
||||||
Sessions
|
|
||||||
{/if}
|
|
||||||
<div class="mt-4 text-center">
|
|
||||||
<p>{status}</p>
|
|
||||||
</div>
|
|
||||||
{#if activeSession}
|
|
||||||
<div>
|
|
||||||
<h3>Active Session:</h3>
|
|
||||||
<p>Node: {activeSession.node}</p>
|
|
||||||
<p>Session Key: {activeSession.sessionKey}</p>
|
|
||||||
<p>Expiration: {activeSession.expiration}</p>
|
|
||||||
</div>
|
|
||||||
{#if sessionStatuses}
|
|
||||||
{#each sessionStatuses as { node, sessionKey, expiration, isExpired }}
|
|
||||||
<p>
|
|
||||||
{isExpired ? "🔴" : "🟢"} Node: {node}, Session Key: {sessionKey},
|
|
||||||
Expiration: {expiration}
|
|
||||||
</p>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
17
src/lib/JWT.svelte
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import { createJWT } from "$lib/services/createJWT";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
|
||||||
|
let jwt = "";
|
||||||
|
|
||||||
|
async function handleCreateClick() {
|
||||||
|
jwt = await createJWT();
|
||||||
|
Cookies.set("token", jwt);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="p-12">
|
||||||
|
<button on:click={handleCreateClick} class="btn variant-filled-primary"
|
||||||
|
>Wundergraph Login</button
|
||||||
|
>
|
||||||
|
</div>
|
5
src/lib/JsonViewer.svelte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
export let json;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<pre>{JSON.stringify(json, null, 2)}</pre>
|
@ -1,17 +0,0 @@
|
|||||||
<script>
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
|
|
||||||
async function login() {
|
|
||||||
const response = await fetch("/api/auth", { method: "POST" });
|
|
||||||
if (!response.ok) {
|
|
||||||
alert("Login failed");
|
|
||||||
} else {
|
|
||||||
let { token } = await response.json();
|
|
||||||
Cookies.set("token", token);
|
|
||||||
alert(`Login Success: ${token}`);
|
|
||||||
console.log(token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<button on:click={login}>Login</button>
|
|
14
src/lib/MailViewer.svelte
Normal 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 }} />
|
@ -1,106 +1,62 @@
|
|||||||
<!-- SignVerifyMessage.svelte -->
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ethers } from "ethers";
|
import { signMessageWithPKP } from "$lib/services/signMessage";
|
||||||
import { onMount } from "svelte";
|
import { walletState, signRequest } from "$lib/stores.js";
|
||||||
|
|
||||||
export let messageToSign = {};
|
|
||||||
|
|
||||||
let currentPKP;
|
let currentPKP;
|
||||||
let sessionSigs;
|
let sessionSigs;
|
||||||
let status = "";
|
let message;
|
||||||
let litNodeClient;
|
let signature;
|
||||||
let messageSignature;
|
let status = "WAITING FOR SIGNATURE";
|
||||||
|
|
||||||
onMount(async () => {
|
walletState.subscribe((value) => {
|
||||||
litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
|
currentPKP = value.pkps[0];
|
||||||
await litNodeClient.connect();
|
sessionSigs = value.sessionSigs;
|
||||||
|
|
||||||
const sessionSigsLocalStorage = localStorage.getItem("google-session");
|
|
||||||
const currentPKPLocalStorage = localStorage.getItem("current-pkp");
|
|
||||||
if (sessionSigsLocalStorage && currentPKPLocalStorage) {
|
|
||||||
sessionSigs = JSON.parse(sessionSigsLocalStorage);
|
|
||||||
currentPKP = JSON.parse(currentPKPLocalStorage);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function signMessageWithPKP() {
|
signRequest.subscribe((value) => {
|
||||||
const userConfirmed = window.confirm(
|
message = value.messageToSign;
|
||||||
"Do you want to sign the following message?\n\n" +
|
signature = value.signature;
|
||||||
JSON.stringify(messageToSign, null, 2)
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (!userConfirmed) {
|
async function signMessage() {
|
||||||
status = "User did not allow to sign the message.";
|
const result = await signMessageWithPKP(sessionSigs, currentPKP, message);
|
||||||
dispatch("status", status);
|
if (result.error) {
|
||||||
return;
|
console.error(result.error);
|
||||||
|
} else {
|
||||||
|
(status = "SIGNED"),
|
||||||
|
signRequest.set({
|
||||||
|
messageToSign: message,
|
||||||
|
signature: result.messageSignature,
|
||||||
|
drawer: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
try {
|
}
|
||||||
// Create a specific JSON object
|
|
||||||
const jsonString = JSON.stringify(messageToSign);
|
|
||||||
|
|
||||||
// Convert the JSON string to an array of character codes
|
function declineSign() {
|
||||||
const toSign = ethers.getBytes(ethers.hashMessage(jsonString));
|
signRequest.set({
|
||||||
|
messageToSign: {},
|
||||||
const litActionCode = `
|
signature: null,
|
||||||
const go = async () => {
|
drawer: false,
|
||||||
const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName });
|
});
|
||||||
};
|
|
||||||
go();
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Sign message
|
|
||||||
const results = await litNodeClient.executeJs({
|
|
||||||
code: litActionCode,
|
|
||||||
sessionSigs: sessionSigs,
|
|
||||||
jsParams: {
|
|
||||||
toSign: toSign,
|
|
||||||
publicKey: currentPKP.publicKey,
|
|
||||||
sigName: "sig1",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get signature
|
|
||||||
const result = results.signatures["sig1"];
|
|
||||||
messageSignature = ethers.Signature.from({
|
|
||||||
r: "0x" + result.r,
|
|
||||||
s: "0x" + result.s,
|
|
||||||
v: result.recid,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Display the signed JSON
|
|
||||||
status = JSON.stringify(messageToSign, null, 2);
|
|
||||||
|
|
||||||
// Verify the signature
|
|
||||||
const recoveredAddr = ethers.verifyMessage(jsonString, messageSignature);
|
|
||||||
|
|
||||||
// Check if the address associated with the signature is the same as the current PKP
|
|
||||||
const verified =
|
|
||||||
currentPKP.ethAddress.toLowerCase() === recoveredAddr.toLowerCase();
|
|
||||||
|
|
||||||
if (verified) {
|
|
||||||
status = "The signature is valid.";
|
|
||||||
} else {
|
|
||||||
status = "The signature is invalid.";
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button on:click={signMessageWithPKP}>Sign Message</button>
|
<div class="flex flex-col items-center justify-center h-full space-y-4">
|
||||||
{#if messageToSign}
|
<p class="text-sm font-bold">{status}</p>
|
||||||
<pre>{JSON.stringify(messageToSign)}</pre>
|
<p class="text-lg break-words max-w-2/3">{JSON.stringify(message)}</p>
|
||||||
{/if}
|
{#if signature}
|
||||||
|
<p class="text-sm font-bold break-words">
|
||||||
{#if status}
|
Signature: {JSON.stringify(signature)}
|
||||||
<div class="mt-4 text-center">
|
</p>
|
||||||
<p>Status: {status}</p>
|
{/if}
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 flex items-center justify-center w-full pb-4 space-x-4"
|
||||||
|
>
|
||||||
|
<button on:click={declineSign} class="btn variant-filled-error"
|
||||||
|
>Decline</button
|
||||||
|
>
|
||||||
|
<button on:click={signMessage} class="btn variant-filled-success"
|
||||||
|
>Sign</button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
{#if messageSignature}
|
|
||||||
<div class="mt-4 text-center">
|
|
||||||
<h3>Signature</h3>
|
|
||||||
<pre>{JSON.stringify(messageSignature)}</pre>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import Cookies from "js-cookie";
|
|
||||||
let user = null;
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const token = Cookies.get("token");
|
|
||||||
const response = await fetch("/api/auth/session", {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
user = await response.json();
|
|
||||||
} else {
|
|
||||||
console.error("Failed to fetch user data");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if user}
|
|
||||||
<p>Welcome, {user.name}!</p>
|
|
||||||
<p>Your roles: {user.roles.join(", ")}</p>
|
|
||||||
{:else}
|
|
||||||
<p>Loading...</p>
|
|
||||||
{/if}
|
|
126
src/lib/Wallet.svelte
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<script>
|
||||||
|
import { useMachine } from "@xstate/svelte";
|
||||||
|
import walletMachine from "./machines/walletMachine";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import Icon from "@iconify/svelte";
|
||||||
|
import { walletState, signRequest } from "./stores";
|
||||||
|
import {
|
||||||
|
signInWithGoogle,
|
||||||
|
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);
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if ($state.context.pkps && $state.context.sessionSigs) {
|
||||||
|
localStorage.setItem(
|
||||||
|
"me",
|
||||||
|
JSON.stringify({
|
||||||
|
pkps: $state.context.pkps,
|
||||||
|
sessionSigs: $state.context.sessionSigs,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMount(() => {
|
||||||
|
const me = JSON.parse(localStorage.getItem("me"));
|
||||||
|
if (me && me.pkps && me.sessionSigs) {
|
||||||
|
send({ type: "RELOAD", ...me });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function startSignIn() {
|
||||||
|
startSignInService.set(true);
|
||||||
|
await signInWithGoogle();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSession() {
|
||||||
|
send("LOGOUT");
|
||||||
|
}
|
||||||
|
|
||||||
|
function signRequestTrigger() {
|
||||||
|
signRequest.set({
|
||||||
|
status: "SIGN REQUEST",
|
||||||
|
messageToSign: { hello: "test" },
|
||||||
|
signature: null,
|
||||||
|
drawer: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if ($signRequest.drawer) {
|
||||||
|
const settings = { position: "bottom", id: "signMessage" };
|
||||||
|
drawerStore.open(settings);
|
||||||
|
} else {
|
||||||
|
drawerStore.close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $state.matches("sessionAvailable") || $state.matches("creatingSession") || $state.matches("signIn")}
|
||||||
|
{#if $state.matches("signIn")}
|
||||||
|
<div class="flex items-center justify-center pb-4">
|
||||||
|
<div class="flex flex-col items-center w-1/3">
|
||||||
|
<button
|
||||||
|
on:click={startSignIn}
|
||||||
|
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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if $state.context.pkps}
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if $state.matches("sessionExpired")}
|
||||||
|
<div class="p-10 bg-white">
|
||||||
|
<p>Error creating session. Please try again.</p>
|
||||||
|
<pre>{JSON.stringify($state.context.error, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<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}
|
@ -11,7 +11,7 @@ export async function createLitSession(
|
|||||||
pkpPublicKey,
|
pkpPublicKey,
|
||||||
authMethod,
|
authMethod,
|
||||||
sessionSigsParams: {
|
sessionSigsParams: {
|
||||||
chain: 'ethereum',
|
chain: 'xdai',
|
||||||
resourceAbilityRequests: [
|
resourceAbilityRequests: [
|
||||||
{
|
{
|
||||||
resource: litResource,
|
resource: litResource,
|
||||||
|
10
src/lib/layouts/HeaderMain.svelte
Normal 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>
|
116
src/lib/machines/walletMachine.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// src/lib/machines/walletMachine.ts
|
||||||
|
import { createMachine, assign } from 'xstate';
|
||||||
|
import { signInWithGoogle } from '../services/signInWithGoogle';
|
||||||
|
import { createSession } from '../services/createSession';
|
||||||
|
|
||||||
|
const walletMachine = createMachine({
|
||||||
|
id: 'wallet',
|
||||||
|
initial: 'signIn',
|
||||||
|
context: {
|
||||||
|
provider: null,
|
||||||
|
providerName: null,
|
||||||
|
authMethod: null,
|
||||||
|
pkps: [],
|
||||||
|
sessionSigs: null,
|
||||||
|
redirect: false
|
||||||
|
},
|
||||||
|
states: {
|
||||||
|
signIn: {
|
||||||
|
on: {
|
||||||
|
RELOAD: {
|
||||||
|
target: 'sessionAvailable',
|
||||||
|
actions: assign({
|
||||||
|
pkps: (_, event) => event.pkps,
|
||||||
|
sessionSigs: (_, event) => event.sessionSigs,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
invoke: {
|
||||||
|
src: 'signInWithGoogle',
|
||||||
|
onDone: {
|
||||||
|
target: 'authenticated',
|
||||||
|
actions: assign({
|
||||||
|
providerName: (_, event) => event.data.providerName,
|
||||||
|
provider: (_, event) => event.data.provider,
|
||||||
|
authMethod: (_, event) => event.data.authMethod,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
authenticated: {
|
||||||
|
invoke: {
|
||||||
|
src: async (context) => {
|
||||||
|
const pkps = await context.provider.fetchPKPsThroughRelayer(context.authMethod);
|
||||||
|
if (pkps.length === 0) {
|
||||||
|
const newPKP = await context.provider.mintPKP(context.authMethod);
|
||||||
|
pkps.push(newPKP);
|
||||||
|
}
|
||||||
|
return pkps;
|
||||||
|
},
|
||||||
|
onDone: {
|
||||||
|
target: 'creatingSession',
|
||||||
|
actions: [
|
||||||
|
assign({
|
||||||
|
pkps: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
onError: 'authenticated',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
creatingSession: {
|
||||||
|
invoke: {
|
||||||
|
src: async (context) => {
|
||||||
|
const { pkps, sessionSigs } = await createSession(context.provider, context.authMethod, context.pkps);
|
||||||
|
return { pkps, sessionSigs };
|
||||||
|
},
|
||||||
|
onDone: {
|
||||||
|
target: 'sessionAvailable',
|
||||||
|
actions: [
|
||||||
|
assign({
|
||||||
|
pkps: (_, event) => event.data.pkps,
|
||||||
|
sessionSigs: (_, event) => event.data.sessionSigs,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
target: 'sessionExpired',
|
||||||
|
actions: assign({
|
||||||
|
error: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sessionAvailable: {
|
||||||
|
on: {
|
||||||
|
EXPIRED: {
|
||||||
|
target: 'sessionExpired',
|
||||||
|
cond: (context) => context.sessionSigs && Object.values(context.sessionSigs).every(sig => sig.expired)
|
||||||
|
},
|
||||||
|
LOGOUT: 'sessionExpired'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sessionExpired: {
|
||||||
|
entry: assign({
|
||||||
|
sessionSigs: null,
|
||||||
|
redirect: true
|
||||||
|
}),
|
||||||
|
after: {
|
||||||
|
0: {
|
||||||
|
target: 'signIn',
|
||||||
|
actions: () => {
|
||||||
|
localStorage.removeItem('me');
|
||||||
|
window.location.href = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
services: {
|
||||||
|
signInWithGoogle,
|
||||||
|
createSession
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default walletMachine;
|
18
src/lib/mintPkp.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { IRelayPKP } from '@lit-protocol/types';
|
||||||
|
import type { IProvider } from '$lib/IProvider';
|
||||||
|
|
||||||
|
export async function mintPkp(provider: IProvider, authMethod: any): Promise<IRelayPKP> {
|
||||||
|
|
||||||
|
const txHash: string = await provider.mintPKPThroughRelayer(authMethod);
|
||||||
|
const response = await provider.relay.pollRequestUntilTerminalState(txHash);
|
||||||
|
if (response.status !== 'Succeeded') {
|
||||||
|
throw new Error('Minting failed');
|
||||||
|
}
|
||||||
|
const newPKP: IRelayPKP = {
|
||||||
|
tokenId: response.pkpTokenId,
|
||||||
|
publicKey: response.pkpPublicKey,
|
||||||
|
ethAddress: response.pkpEthAddress
|
||||||
|
};
|
||||||
|
|
||||||
|
return newPKP;
|
||||||
|
}
|
8
src/lib/mockApps.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const mockApps = [
|
||||||
|
{ name: 'cloud' },
|
||||||
|
{ name: 'pass'},
|
||||||
|
{ name: 'directus' },
|
||||||
|
{ name: 'penpot'},
|
||||||
|
{ name: 'paperless'}
|
||||||
|
];
|
||||||
|
|
57
src/lib/services/createJWT.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { LitNodeClient } from "@lit-protocol/lit-node-client";
|
||||||
|
import type { AccsEVMParams } from "@lit-protocol/types";
|
||||||
|
|
||||||
|
export const createJWT = async () => {
|
||||||
|
|
||||||
|
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 resourceId = {
|
||||||
|
baseUrl: "https://localhost:3000/",
|
||||||
|
path: "wunderauth",
|
||||||
|
orgId: "°",
|
||||||
|
role: "owner",
|
||||||
|
extraData: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionSigs = me.sessionSigs;
|
||||||
|
|
||||||
|
const unifiedAccessControlConditions: AccsEVMParams[] = [
|
||||||
|
{
|
||||||
|
conditionType: 'evmBasic',
|
||||||
|
contractAddress: '',
|
||||||
|
standardContractType: '',
|
||||||
|
chain: 'xdai',
|
||||||
|
method: '',
|
||||||
|
parameters: [
|
||||||
|
':userAddress',
|
||||||
|
],
|
||||||
|
returnValueTest: {
|
||||||
|
comparator: '=',
|
||||||
|
value: '0x4b975F10baf1153A5CC688B52d55809cd2d8BB57'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
await litNodeClient.saveSigningCondition({
|
||||||
|
unifiedAccessControlConditions,
|
||||||
|
sessionSigs,
|
||||||
|
resourceId,
|
||||||
|
chain: "litSessionSign",
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const jwt = await litNodeClient.getSignedToken({
|
||||||
|
unifiedAccessControlConditions,
|
||||||
|
chain: 'xdai',
|
||||||
|
sessionSigs,
|
||||||
|
resourceId
|
||||||
|
});
|
||||||
|
|
||||||
|
return jwt;
|
||||||
|
};
|
25
src/lib/services/createSession.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// src/lib/services/createSession.ts
|
||||||
|
import { createLitSession } from '$lib/createLitSession';
|
||||||
|
import type { IRelayPKP } from "@lit-protocol/types";
|
||||||
|
|
||||||
|
export const createSession = async (provider, authMethod, pkps: IRelayPKP[]) => {
|
||||||
|
try {
|
||||||
|
let currentPKP;
|
||||||
|
if (pkps.length === 0) {
|
||||||
|
currentPKP = await provider.mintPKP(authMethod);
|
||||||
|
pkps = [...pkps, currentPKP];
|
||||||
|
} else {
|
||||||
|
currentPKP = pkps[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionSigs = await createLitSession(
|
||||||
|
provider,
|
||||||
|
currentPKP.publicKey,
|
||||||
|
authMethod,
|
||||||
|
);
|
||||||
|
|
||||||
|
return { pkps, sessionSigs };
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to create session: ${error.message}`);
|
||||||
|
}
|
||||||
|
};
|
66
src/lib/services/mutateAccessControlConditions.ts
Normal 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));
|
||||||
|
};
|
30
src/lib/services/sendTxWithPKPWallet.ts
Normal 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');
|
||||||
|
}
|
38
src/lib/services/signInWithGoogle.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// src/lib/services/signInWithGoogle.ts
|
||||||
|
import { connectProvider } from "$lib/setupLit";
|
||||||
|
import { isSignInRedirect, getProviderFromUrl } from "@lit-protocol/lit-auth-client";
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export let startSignIn = writable(false);
|
||||||
|
|
||||||
|
let providerName;
|
||||||
|
|
||||||
|
export const signInWithGoogle = async () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
try {
|
||||||
|
let provider = await connectProvider();
|
||||||
|
if (isSignInRedirect("http://localhost:3000/")) {
|
||||||
|
providerName = getProviderFromUrl();
|
||||||
|
if (providerName) {
|
||||||
|
const authMethod = await provider.authenticate();
|
||||||
|
return { authMethod, provider, providerName };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let shouldStartSignIn = false;
|
||||||
|
startSignIn.subscribe(value => {
|
||||||
|
shouldStartSignIn = value;
|
||||||
|
});
|
||||||
|
if (!providerName && shouldStartSignIn) {
|
||||||
|
await provider.signIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error during sign-in:', err);
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
startSignIn.set(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Cannot sign in with Google in a non-browser environment.");
|
||||||
|
}
|
||||||
|
};
|
41
src/lib/services/signMessage.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// $lib/services/signMessage.ts
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
export async function signMessageWithPKP(sessionSigs, currentPKP, messageToSign) {
|
||||||
|
try {
|
||||||
|
const jsonString = JSON.stringify(messageToSign);
|
||||||
|
const toSign = ethers.getBytes(ethers.hashMessage(jsonString));
|
||||||
|
|
||||||
|
const litActionCode = `
|
||||||
|
const go = async () => {
|
||||||
|
const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName });
|
||||||
|
};
|
||||||
|
go();
|
||||||
|
`;
|
||||||
|
|
||||||
|
const litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
|
||||||
|
await litNodeClient.connect();
|
||||||
|
|
||||||
|
const results = await litNodeClient.executeJs({
|
||||||
|
code: litActionCode,
|
||||||
|
sessionSigs: sessionSigs,
|
||||||
|
jsParams: {
|
||||||
|
toSign: toSign,
|
||||||
|
publicKey: currentPKP.publicKey,
|
||||||
|
sigName: "sig1",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = results.signatures["sig1"];
|
||||||
|
const messageSignature = ethers.Signature.from({
|
||||||
|
r: "0x" + result.r,
|
||||||
|
s: "0x" + result.s,
|
||||||
|
v: result.recid,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { messageSignature };
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return { error: err };
|
||||||
|
}
|
||||||
|
}
|
58
src/lib/setupChainProvider.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { configureChains, createConfig } from '@wagmi/core';
|
||||||
|
import { gnosis } from '@wagmi/core/chains';
|
||||||
|
import { publicProvider } from '@wagmi/core/providers/public';
|
||||||
|
import { InjectedConnector } from '@wagmi/core/connectors/injected';
|
||||||
|
import { jsonRpcProvider } from '@wagmi/core/providers/jsonRpc';
|
||||||
|
|
||||||
|
// const chronicleChain = {
|
||||||
|
// id: 175177,
|
||||||
|
// name: 'Chronicle',
|
||||||
|
// network: 'chronicle',
|
||||||
|
|
||||||
|
// nativeCurrency: {
|
||||||
|
// decimals: 18,
|
||||||
|
// name: 'Chronicle - Lit Protocol Testnet',
|
||||||
|
// symbol: 'LIT'
|
||||||
|
// },
|
||||||
|
// rpcUrls: {
|
||||||
|
// default: {
|
||||||
|
// http: ['https://chain-rpc.litprotocol.com/http']
|
||||||
|
// },
|
||||||
|
// public: {
|
||||||
|
// http: ['https://chain-rpc.litprotocol.com/http']
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// blockExplorers: {
|
||||||
|
// default: {
|
||||||
|
// name: 'Chronicle - Lit Protocol Testnet',
|
||||||
|
// url: 'https://chain.litprotocol.com'
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// testnet: true
|
||||||
|
// };
|
||||||
|
|
||||||
|
export function initChainProvider() {
|
||||||
|
const { chains, publicClient } = configureChains(
|
||||||
|
[gnosis],
|
||||||
|
[
|
||||||
|
jsonRpcProvider({
|
||||||
|
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] })
|
||||||
|
}),
|
||||||
|
jsonRpcProvider({
|
||||||
|
rpc: () => ({
|
||||||
|
http: `https://rpc.ankr.com/gnosis`,
|
||||||
|
wss: `wss://rpc.gnosischain.com/wss`
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
publicProvider()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
createConfig({
|
||||||
|
autoConnect: true,
|
||||||
|
connectors: [
|
||||||
|
new InjectedConnector({ chains }),
|
||||||
|
],
|
||||||
|
publicClient
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
15
src/lib/stores.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const redirectStore = writable(false);
|
||||||
|
|
||||||
|
export const walletState = writable(null);
|
||||||
|
|
||||||
|
export const signRequest = writable({
|
||||||
|
messageToSign: {},
|
||||||
|
signature: null,
|
||||||
|
drawer: false
|
||||||
|
});
|
||||||
|
|
||||||
|
export const googleSession = writable({
|
||||||
|
activeSession: false
|
||||||
|
});
|
@ -1,18 +1,85 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import "../app.css";
|
||||||
import { QueryClientProvider } from "@tanstack/svelte-query";
|
import { QueryClientProvider } from "@tanstack/svelte-query";
|
||||||
import type { LayoutData } from "./$types";
|
import type { LayoutData } from "./$types";
|
||||||
import { client } from "$lib/wundergraph";
|
import { client } from "$lib/wundergraph";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { initChainProvider } from "$lib/setupChainProvider";
|
||||||
|
import { googleSession } from "$lib/stores.js";
|
||||||
|
import Wallet from "$lib/Wallet.svelte";
|
||||||
|
import { Drawer, initializeStores } from "@skeletonlabs/skeleton";
|
||||||
|
import { getDrawerStore } from "@skeletonlabs/skeleton";
|
||||||
|
import Signer from "$lib/Signer.svelte";
|
||||||
|
|
||||||
|
initializeStores();
|
||||||
|
|
||||||
|
const drawerStore = getDrawerStore();
|
||||||
|
|
||||||
|
let activeSession = false;
|
||||||
|
|
||||||
export let data: LayoutData;
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
initChainProvider();
|
||||||
|
});
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
client.setAuthorizationToken(token);
|
client.setAuthorizationToken(token);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<QueryClientProvider client={data.queryClient}>
|
<Drawer>
|
||||||
<slot />
|
{#if $drawerStore.id === "signMessage"}
|
||||||
</QueryClientProvider>
|
<Signer messageToSign={{ hello: "me" }} />
|
||||||
|
{:else}
|
||||||
|
<!-- (fallback contents) -->
|
||||||
|
{/if}</Drawer
|
||||||
|
>
|
||||||
|
<div class="grid h-screen grid-layout bg-color">
|
||||||
|
<QueryClientProvider client={data.queryClient} class="main">
|
||||||
|
<slot />
|
||||||
|
</QueryClientProvider>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<Wallet />
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.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>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { QueryClient } from '@tanstack/svelte-query';
|
import { QueryClient } from '@tanstack/svelte-query';
|
||||||
import type { LayoutLoad } from './$types';
|
import type { LayoutLoad } from './$types';
|
||||||
|
export const prerender = true
|
||||||
|
export const ssr = false
|
||||||
|
|
||||||
export const load: LayoutLoad = async () => {
|
export const load: LayoutLoad = async () => {
|
||||||
const queryClient = new QueryClient({
|
const queryClient = new QueryClient({
|
||||||
@ -10,6 +12,5 @@ export const load: LayoutLoad = async () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { queryClient };
|
return { queryClient };
|
||||||
};
|
};
|
||||||
|
10
src/routes/+page.server.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { ServerLoad } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const load: ServerLoad = async ({ locals }) => {
|
||||||
|
await locals.session.set({ myPKP: "hello1" });
|
||||||
|
|
||||||
|
return {
|
||||||
|
myPKP: locals.session.data.myPKP
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,37 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
import GoogleAuth from "$lib/GoogleAuth.svelte";
|
// import { signRequest } from "$lib/stores.js";
|
||||||
|
|
||||||
|
// function trigger() {
|
||||||
|
// signRequest.set({ json: { hello: "test" } });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// import type { PageData } from "./$types";
|
||||||
|
// export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<GoogleAuth />
|
<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> -->
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
const secretKey = 'mysecrettestkey';
|
|
||||||
|
|
||||||
export async function POST() {
|
|
||||||
const token = jwt.sign({ name: 'Samuel', loggedIn: true, roles: ['admin'] }, secretKey);
|
|
||||||
if (!token) {
|
|
||||||
throw error(400, 'No token created.');
|
|
||||||
}
|
|
||||||
return new Response(JSON.stringify({ token }), { status: 200 });
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
// src/routes/api/session/+server.ts
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
const secretKey = 'mysecrettestkey';
|
|
||||||
|
|
||||||
export async function GET({ request }) {
|
|
||||||
const authHeader = request.headers.get('Authorization');
|
|
||||||
if (!authHeader) {
|
|
||||||
throw error(401, 'No Authorization header provided.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = authHeader.split(' ')[1];
|
|
||||||
if (!token) {
|
|
||||||
throw error(401, 'No token provided.');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const user = jwt.verify(token, secretKey);
|
|
||||||
return new Response(JSON.stringify(user), { status: 200 });
|
|
||||||
} catch (err) {
|
|
||||||
throw error(401, 'Invalid token.');
|
|
||||||
}
|
|
||||||
}
|
|
34
src/routes/api/chat/+server.ts
Normal 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;
|
95
src/routes/me/+layout.svelte
Normal 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>
|
@ -1,24 +1,48 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createQuery } from "../../lib/wundergraph";
|
import HeaderMain from "$lib/layouts/HeaderMain.svelte";
|
||||||
import Login from "$lib/Login.svelte";
|
import { Avatar } from "@skeletonlabs/skeleton";
|
||||||
import User from "$lib/User.svelte";
|
|
||||||
|
|
||||||
const projectsQuery = createQuery({
|
import { createQuery } from "../../lib/wundergraph";
|
||||||
operationName: "Projects",
|
|
||||||
|
const meQuery = createQuery({
|
||||||
|
operationName: "Me",
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Login />
|
<HeaderMain>
|
||||||
<User />
|
<div slot="header">
|
||||||
|
<h1>Profile</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br />
|
<div slot="main" class="h-full w-full overflow-scroll">
|
||||||
Projects
|
<div class="w-full h-full overflow-scroll">
|
||||||
<div class="results">
|
{#if $meQuery.isLoading}
|
||||||
{#if $projectsQuery.isLoading}
|
<p>Loading...</p>
|
||||||
<p>Loading...</p>
|
{:else if $meQuery.error}
|
||||||
{:else if $projectsQuery.error}
|
<pre>Error: {JSON.stringify($meQuery.error, null, 2)}</pre>
|
||||||
<pre>Error: {JSON.stringify($projectsQuery.error, null, 2)}</pre>
|
{:else}
|
||||||
{:else}
|
<div class="container mx-auto p-8 space-y-8">
|
||||||
<pre>{JSON.stringify($projectsQuery.data, null, 2)}</pre>
|
<div class="flex items-center space-x-4">
|
||||||
{/if}
|
<Avatar
|
||||||
</div>
|
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>
|
||||||
|
14
src/routes/me/acc/+page.svelte
Normal 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>
|
13
src/routes/me/apps/+page.svelte
Normal 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>
|
12
src/routes/me/apps/[name]/+page.js
Normal 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 };
|
||||||
|
}
|
10
src/routes/me/apps/[name]/+page.svelte
Normal 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%"
|
||||||
|
/>
|
98
src/routes/me/banking/+page.svelte
Normal 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>
|
40
src/routes/me/bookmarks/+page.svelte
Normal 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>
|
5
src/routes/me/chatGPT/+page.svelte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<script>
|
||||||
|
import Ai from "$lib/Ai.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Ai />
|
36
src/routes/me/contacts/+page.svelte
Normal 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>
|
92
src/routes/me/conversations/+page.svelte
Normal 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>
|
57
src/routes/me/documents/+page.svelte
Normal 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>
|
67
src/routes/me/paperless/+page.svelte
Normal 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>
|
26
src/routes/me/projects/+page.svelte
Normal 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>
|
34
src/routes/server/api/chat/+server.ts
Normal 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;
|
34
src/routes/server/wundergraph/+server.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { verifyJwt } from "@lit-protocol/lit-node-client";
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export async function GET({ request }) {
|
||||||
|
const authHeader = request.headers.get('Authorization');
|
||||||
|
if (!authHeader) {
|
||||||
|
throw error(401, 'No Authorization header provided.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
if (!token) {
|
||||||
|
throw error(401, 'No jwt provided.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { verified, payload } = await verifyJwt({ jwt: token });
|
||||||
|
if (
|
||||||
|
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 matching")
|
||||||
|
}
|
||||||
|
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 error")
|
||||||
|
}
|
||||||
|
}
|
BIN
static/lake.jpeg
Normal file
After Width: | Height: | Size: 1.3 MiB |
@ -1,4 +1,4 @@
|
|||||||
import adapter from '@sveltejs/adapter-auto';
|
import adapter from '@sveltejs/adapter-static'
|
||||||
import { vitePreprocess } from '@sveltejs/kit/vite';
|
import { vitePreprocess } from '@sveltejs/kit/vite';
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
@ -13,6 +13,7 @@ const config = {
|
|||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
32
tailwind.config.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
// 2. Opt for dark mode to be handled via the class method
|
||||||
|
darkMode: 'class',
|
||||||
|
content: [
|
||||||
|
'./src/**/*.{html,js,svelte,ts}',
|
||||||
|
// 3. Append the path to the Skeleton package
|
||||||
|
join(require.resolve(
|
||||||
|
'@skeletonlabs/skeleton'),
|
||||||
|
'../**/*.{html,js,svelte,ts}'
|
||||||
|
)
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
forms,
|
||||||
|
// 4. Append the Skeleton plugin (after other plugins)
|
||||||
|
skeleton({
|
||||||
|
themes: { preset: ["wintry"] }
|
||||||
|
})
|
||||||
|
]
|
||||||
|
} satisfies Config;
|
||||||
|
|
||||||
|
export default config;
|
@ -1,6 +1,6 @@
|
|||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import { fetchSchema } from './.wundergraph/schemas/fetch-schemas';
|
import { fetchSchemas } from './.wundergraph/schemas/fetch-schemas';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
@ -15,4 +15,4 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|