diff --git a/docker-compose-hasura.yml b/docker-compose-hasura.yml index 103b8f94..19280a55 100644 --- a/docker-compose-hasura.yml +++ b/docker-compose-hasura.yml @@ -1,4 +1,3 @@ -version: '3.6' services: postgres: image: postgres:15 @@ -29,7 +28,7 @@ services: ## uncomment next line to set an admin secret HASURA_GRAPHQL_ADMIN_SECRET: z6KTncr37daLn5Vq HASURA_GRAPHQL_METADATA_DEFAULTS: '{"backend_configs":{"dataconnector":{"athena":{"uri":"http://data-connector-agent:8081/api/v1/athena"},"mariadb":{"uri":"http://data-connector-agent:8081/api/v1/mariadb"},"mysql8":{"uri":"http://data-connector-agent:8081/api/v1/mysql"},"oracle":{"uri":"http://data-connector-agent:8081/api/v1/oracle"},"snowflake":{"uri":"http://data-connector-agent:8081/api/v1/snowflake"}}}}' - HASURA_GRAPHQL_JWT_SECRET: '{"type": "RS256", "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAu4H5cKl1P1wnZB9mUgLjUAtyA7kmlzlJwXwUBwPIInxP3kKWJIF8\no4oW4EO5d75qeRjUueYPVh6WCe0OHrOyoFNcd0VsTkuYVMyXlPYlQM1HmBA64b5r\nC7nmr+mk4xlBE6i3EPx1kQAUJTR147WqeFvS3PeWaQVwocffb1KZjfVks3+3WOYA\nNkxoYiDmxGwi+MShzOSyvYizxBHPFm8WgTUhHWPiBCHkX+CRXlz3R7k2nDVxnD7s\nnDDJ8ITxxeY3DJVH4qvARNE+cbFyLbQsmnw72zzsBQmuTt7qLXdQlEmYxxgBZceE\nilkPJOtV11LM/S/CzZOkYZg+Df2F9+PggwIDAQAB\n-----END RSA PUBLIC KEY-----"}' + HASURA_GRAPHQL_JWT_SECRET: '{"type": "RS256", "key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAs4q6OBeCTUhVWUTGa3+uw7d2lWbPrIKjU2hvSlvOQE1SIK0AtUP+\nTagi2xIQpNj05j+yb8hh2fX0SqcWmTBGkZksMecesThpYd+YQ30yDHGoI8F70l8D\nRbytmO6habfPqjx1qvXYUscyWycDZyEM0KIxvgQhhR/Nf1CfReIMHmFQtgmTIRnZ\n6nwHXVrHBbVN5LtUYuY4QNpILgwDH0mJAQ0//Fasr0vq5WwkEHlJ5Grzv1jkl6Y8\nERvaNhfvh4vm2lSiS6RkwgWB6wvdNKy2j1AxKmHl2Xdt9aoGsqC5G8nvrmICeEgL\nFTMpJ+nswMIpDqSmYhoraMmnUqoAvNegNwIDAQAB\n-----END RSA PUBLIC KEY-----"}' HASURA_GRAPHQL_UNAUTHORIZED_ROLE: 'anonymous' ACTION_API_DOMAIN: 'http://172.27.10.137:3000' # HASURA_GRAPHQL_ENABLED_APIS: graphql diff --git a/hasura/metadata/databases/punkga-pg/tables/public_authorizer_users.yaml b/hasura/metadata/databases/punkga-pg/tables/public_authorizer_users.yaml index 4a6a134e..507cdc3b 100644 --- a/hasura/metadata/databases/punkga-pg/tables/public_authorizer_users.yaml +++ b/hasura/metadata/databases/punkga-pg/tables/public_authorizer_users.yaml @@ -91,6 +91,7 @@ select_permissions: - revoked_timestamp - roles - signup_methods + - ton_wallet_address - updated_at - wallet_address computed_fields: @@ -109,6 +110,7 @@ update_permissions: - gender - nickname - picture + - ton_wallet_address - wallet_address filter: id: diff --git a/hasura/metadata/databases/punkga-pg/tables/public_top_creator_donate.yaml b/hasura/metadata/databases/punkga-pg/tables/public_top_creator_donate.yaml new file mode 100644 index 00000000..2d69074a --- /dev/null +++ b/hasura/metadata/databases/punkga-pg/tables/public_top_creator_donate.yaml @@ -0,0 +1,3 @@ +table: + name: top_creator_donate + schema: public diff --git a/hasura/metadata/databases/punkga-pg/tables/public_top_user_donate.yaml b/hasura/metadata/databases/punkga-pg/tables/public_top_user_donate.yaml new file mode 100644 index 00000000..af8b39da --- /dev/null +++ b/hasura/metadata/databases/punkga-pg/tables/public_top_user_donate.yaml @@ -0,0 +1,3 @@ +table: + name: top_user_donate + schema: public diff --git a/hasura/metadata/query_collections.yaml b/hasura/metadata/query_collections.yaml index 155e03bf..27f5b73f 100644 --- a/hasura/metadata/query_collections.yaml +++ b/hasura/metadata/query_collections.yaml @@ -1077,70 +1077,6 @@ } } } - - name: User - Get Profile - query: | - query GetUserProfile { - authorizer_users(limit: 1) { - id - email - email_verified_at - bio - birthdate - gender - active_wallet_address: active_evm_address - wallet_address - nickname - picture - signup_methods - levels { - xp - level - user_level_chain { - id - name - punkga_config - } - } - authorizer_users_user_wallet { - address - } - user_quests_aggregate { - aggregate { - count - } - } - user_quests(order_by: {created_at:desc}, limit: 20) { - created_at - status - user_quest_rewards { - tx_hash - } - quest { - id - name - quests_campaign { - campaign_chain { - punkga_config - } - } - quests_i18n { - id - quest_id - language_id - data - i18n_language { - id - description - icon - is_main - symbol - } - } - reward - } - } - } - } - name: Public - query artwork by creator query: | query artworks ($creator_id: Int!, $limit: Int = 10, $offset: Int = 0) { @@ -1469,3 +1405,68 @@ } } } + - name: User - Get Profile + query: | + query GetUserProfile { + authorizer_users(limit: 1) { + id + email + email_verified_at + bio + birthdate + gender + active_wallet_address: active_evm_address + wallet_address + ton_wallet_address + nickname + picture + signup_methods + levels { + xp + level + user_level_chain { + id + name + punkga_config + } + } + authorizer_users_user_wallet { + address + } + user_quests_aggregate { + aggregate { + count + } + } + user_quests(order_by: {created_at:desc}, limit: 20) { + created_at + status + user_quest_rewards { + tx_hash + } + quest { + id + name + quests_campaign { + campaign_chain { + punkga_config + } + } + quests_i18n { + id + quest_id + language_id + data + i18n_language { + id + description + icon + is_main + symbol + } + } + reward + } + } + } + } diff --git a/hasura/migrations/punkga-pg/1726730858341_top_user_donate/down.sql b/hasura/migrations/punkga-pg/1726730858341_top_user_donate/down.sql new file mode 100644 index 00000000..3b4c4465 --- /dev/null +++ b/hasura/migrations/punkga-pg/1726730858341_top_user_donate/down.sql @@ -0,0 +1,12 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE OR REPLACE VIEW "public"."top_user_donate" AS +-- SELECT +-- telegram_users.username, +-- telegram_users.user_id, +-- donate_history.telegram_id, +-- sum(donate_history.value) AS value +-- FROM donate_history +-- INNER JOIN telegram_users on telegram_users.telegram_id = donate_history.telegram_id +-- GROUP BY telegram_users.username,telegram_users.user_id,donate_history.telegram_id +-- LIMIT 10; diff --git a/hasura/migrations/punkga-pg/1726730858341_top_user_donate/up.sql b/hasura/migrations/punkga-pg/1726730858341_top_user_donate/up.sql new file mode 100644 index 00000000..3d08c22b --- /dev/null +++ b/hasura/migrations/punkga-pg/1726730858341_top_user_donate/up.sql @@ -0,0 +1,10 @@ +CREATE OR REPLACE VIEW "public"."top_user_donate" AS + SELECT + telegram_users.username, + telegram_users.user_id, + donate_history.telegram_id, + sum(donate_history.value) AS value + FROM donate_history + INNER JOIN telegram_users on telegram_users.telegram_id = donate_history.telegram_id + GROUP BY telegram_users.username,telegram_users.user_id,donate_history.telegram_id + LIMIT 10; diff --git a/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/down.sql b/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/down.sql new file mode 100644 index 00000000..bca1b438 --- /dev/null +++ b/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/down.sql @@ -0,0 +1,26 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE +-- OR REPLACE VIEW "public"."top_creator_donate" AS +-- SELECT +-- creators.id, +-- creators.email, +-- creators.name, +-- creators.avatar_url, +-- sum(donate_history.value) AS value +-- FROM +-- ( +-- donate_history +-- JOIN creators ON ( +-- ( +-- creators.id = donate_history.creator_id +-- ) +-- ) +-- ) +-- GROUP BY +-- creators.id, +-- creators.email, +-- creators.name, +-- creators.avatar_url +-- LIMIT +-- 10; diff --git a/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/up.sql b/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/up.sql new file mode 100644 index 00000000..4943814c --- /dev/null +++ b/hasura/migrations/punkga-pg/1726733949687_top_creator_donate/up.sql @@ -0,0 +1,24 @@ +CREATE +OR REPLACE VIEW "public"."top_creator_donate" AS +SELECT + creators.id, + creators.email, + creators.name, + creators.avatar_url, + sum(donate_history.value) AS value +FROM + ( + donate_history + JOIN creators ON ( + ( + creators.id = donate_history.creator_id + ) + ) + ) +GROUP BY + creators.id, + creators.email, + creators.name, + creators.avatar_url +LIMIT + 10; diff --git a/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/down.sql b/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/down.sql new file mode 100644 index 00000000..ffe1cf52 --- /dev/null +++ b/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."authorizer_users" add column "ton_wallet_address" text +-- null; diff --git a/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/up.sql b/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/up.sql new file mode 100644 index 00000000..6b508315 --- /dev/null +++ b/hasura/migrations/punkga-pg/1727059964949_alter_table_public_authorizer_users_add_column_ton_wallet_address/up.sql @@ -0,0 +1,2 @@ +alter table "public"."authorizer_users" add column "ton_wallet_address" text + null; diff --git a/src/modules/telegram/telegram.controller.ts b/src/modules/telegram/telegram.controller.ts index 2a97fc2a..a4339bdc 100644 --- a/src/modules/telegram/telegram.controller.ts +++ b/src/modules/telegram/telegram.controller.ts @@ -94,4 +94,24 @@ export class TelegramController { createAndLink() { return this.telegramSvc.createAndLink(); } + + @UseGuards(AuthGuard, RolesGuard) + @ApiBearerAuth() + @Roles(Role.TelegramUser) + @Post('get-top-donate') + @UseInterceptors(AuthUserInterceptor) + @ApiOperation({ summary: '' }) + getTopDonate() { + return this.telegramSvc.getTopDonate(); + } + + @UseGuards(AuthGuard, RolesGuard) + @ApiBearerAuth() + @Roles(Role.TelegramUser) + @Post('get-top-creator-donate') + @UseInterceptors(AuthUserInterceptor) + @ApiOperation({ summary: '' }) + getTopCreatorDonate() { + return this.telegramSvc.getTopCreatorDonate(); + } } diff --git a/src/modules/telegram/telegram.graphql.ts b/src/modules/telegram/telegram.graphql.ts index 9f563fff..d56ab585 100644 --- a/src/modules/telegram/telegram.graphql.ts +++ b/src/modules/telegram/telegram.graphql.ts @@ -160,7 +160,7 @@ export class TelegramGraphql { this.configSvc.get('graphql.endpoint'), '', `query telegram_quests($telegram_user_id: Int!) { - telegram_quests { + telegram_quests(where: {activated: {_eq: true}, deleted: {_eq: false}}) { id quest_name quest_url @@ -205,7 +205,7 @@ export class TelegramGraphql { this.configSvc.get('graphql.endpoint'), '', `query telegram_quests($id: bigint!, $telegram_user_id: Int!) { - telegram_quests(where: {id: {_eq: $id}}) { + telegram_quests(where: {id: {_eq: $id},activated: {_eq: true}, deleted: {_eq: false}}) { id quest_name quest_url @@ -343,4 +343,51 @@ export class TelegramGraphql { headers ); } + getTopDonate() { + const headers = { + 'x-hasura-admin-secret': this.configSvc.get( + 'graphql.adminSecret' + ), + }; + + return this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query top_user_donate { + top_user_donate(limit: 10) { + user_id + telegram_id + username + value + } + }`, + 'top_user_donate', + {}, + headers + ); + } + getTopCreatorDonate() { + const headers = { + 'x-hasura-admin-secret': this.configSvc.get( + 'graphql.adminSecret' + ), + }; + + return this.graphqlSvc.query( + this.configSvc.get('graphql.endpoint'), + '', + `query top_creator_donate { + top_creator_donate(limit: 10) { + id + name + email + avatar_url + value + } + }`, + 'top_creator_donate', + {}, + headers + ); + } } diff --git a/src/modules/telegram/telegram.service.ts b/src/modules/telegram/telegram.service.ts index ba04454d..8d6bbe7f 100644 --- a/src/modules/telegram/telegram.service.ts +++ b/src/modules/telegram/telegram.service.ts @@ -20,7 +20,7 @@ export class TelegramService { private configService: ConfigService, private telegramGraphql: TelegramGraphql, private jwtService: JwtService - ) {} + ) { } async readChapter(manga_slug: string, chapter_number: number) { try { @@ -136,7 +136,7 @@ export class TelegramService { throw new UnauthorizedException(error.message); } } - async createAndLink(){ + async createAndLink() { const { telegramId, telegramUserId } = ContextProvider.getAuthUser(); const email = `tele_${telegramId}_${(new Date()).getTime()}@punkga.me`; const username = `tele_${telegramId}_${(new Date()).getTime()}`; @@ -150,7 +150,7 @@ export class TelegramService { signup_methods: 'telegram' }) if (insertedUser.errors) return insertedUser; - try { + try { const userId = insertedUser.data?.insert_authorizer_users?.returning[0].id; const updateResult = await this.telegramGraphql.updateTelegramUser({ id: telegramUserId, @@ -211,7 +211,22 @@ export class TelegramService { const quests = await this.telegramGraphql.getTelegramQuest({ telegram_user_id: telegramUserId, }); - + if (quests?.data) { + quests?.data?.telegram_quests?.map((q, i) => { + if (q.type == "Daily" && q.telegram_quest_histories) { + q.telegram_quest_histories = q.telegram_quest_histories.map((h, j) => { + const fullToday = new Date(); + const today = new Date(Date.UTC(fullToday.getUTCFullYear(), fullToday.getUTCMonth(), + fullToday.getUTCDate(), 0, 0, 0)) + if (Date.parse(today.toISOString()) - Date.parse(h.created_date + 'Z') > 0) { + return null; + } + return h; + }) + } + q.telegram_quest_histories = q.telegram_quest_histories.filter(x => x != null); + }) + } return quests; } catch (errors) { return { @@ -220,14 +235,42 @@ export class TelegramService { } } - async saveQuest(id) { + async getQuestById(id) { try { const { telegramUserId } = ContextProvider.getAuthUser(); - let quest; const quests = await this.telegramGraphql.getTelegramQuestById({ id: id, telegram_user_id: telegramUserId, }); + if (quests?.data) { + quests?.data?.telegram_quests?.map((q, i) => { + if (q.type == "Daily" && q.telegram_quest_histories) { + q.telegram_quest_histories = q.telegram_quest_histories.map((h, j) => { + const fullToday = new Date(); + const today = new Date(Date.UTC(fullToday.getUTCFullYear(), fullToday.getUTCMonth(), + fullToday.getUTCDate(), 0, 0, 0)) + if (Date.parse(today.toISOString()) - Date.parse(h.created_date + 'Z') > 0) { + return null; + } + return h; + }) + } + q.telegram_quest_histories = q.telegram_quest_histories.filter(x => x != null); + }) + } + return quests; + } catch (errors) { + return { + errors, + }; + } + } + + async saveQuest(id) { + try { + const { telegramUserId } = ContextProvider.getAuthUser(); + let quest; + const quests = await this.getQuestById(id); if (quests?.data?.telegram_quests.length > 0) { quest = quests?.data?.telegram_quests[0]; @@ -259,8 +302,8 @@ export class TelegramService { quest.claim_after <= 0 || (Date.parse(new Date().toISOString()) - Date.parse(h.created_date + 'Z')) / - 1000 >= - quest.claim_after * 60 + 1000 >= + quest.claim_after * 60 ) { var r = await this.telegramGraphql.updateTelegramQuestHistory({ quest_id: id, @@ -285,10 +328,7 @@ export class TelegramService { }, }; } - var lastResponse = await this.telegramGraphql.getTelegramQuestById({ - id: id, - telegram_user_id: telegramUserId, - }); + var lastResponse = await this.getQuestById(id); return lastResponse?.data?.telegram_quests[0]; } catch (errors) { return { @@ -296,4 +336,27 @@ export class TelegramService { }; } } + + async getTopDonate() { + try { + const topDonate = await this.telegramGraphql.getTopDonate(); + + return topDonate; + } catch (errors) { + return { + errors, + }; + } + } + async getTopCreatorDonate() { + try { + const topDonate = await this.telegramGraphql.getTopCreatorDonate(); + + return topDonate; + } catch (errors) { + return { + errors, + }; + } + } } diff --git a/src/modules/user/dto/update-profile-request.dto.ts b/src/modules/user/dto/update-profile-request.dto.ts index 285050ae..1e39198f 100644 --- a/src/modules/user/dto/update-profile-request.dto.ts +++ b/src/modules/user/dto/update-profile-request.dto.ts @@ -14,6 +14,9 @@ export class UpdateProfileRequestDto { @ApiPropertyOptional({ example: '' }) nickname: string; + @ApiPropertyOptional({ example: '' }) + ton_wallet_address: string; + @ApiPropertyOptional({ type: 'string', format: 'binary' }) picture: Express.Multer.File; } diff --git a/src/modules/user/interfaces/update-profile.interface.ts b/src/modules/user/interfaces/update-profile.interface.ts index 810e29f0..81d17897 100644 --- a/src/modules/user/interfaces/update-profile.interface.ts +++ b/src/modules/user/interfaces/update-profile.interface.ts @@ -5,6 +5,7 @@ export interface IUpdateProfile { gender: string; birthdate: string; nickname: string; + ton_wallet_address: string; picture?: string; }; } diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index f1e30be0..4a951879 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -212,7 +212,7 @@ export class UserService { files: Array ) { try { - const { birthdate, gender, bio, nickname } = data; + const { birthdate, gender, bio, nickname, ton_wallet_address } = data; const { token, userId } = ContextProvider.getAuthUser(); const variables: IUpdateProfile = { @@ -222,25 +222,29 @@ export class UserService { gender, birthdate, nickname, + ton_wallet_address, }, }; - const pictureFile = files.filter((f) => f.fieldname === 'picture')[0]; - if (pictureFile) { - const pictureUrl = await this.filesService.uploadImageToS3( - `user-${userId}`, - pictureFile - ); + if (files && files.length > 0) { + const pictureFile = files.filter((f) => f.fieldname === 'picture')[0]; + if (pictureFile) { + const pictureUrl = await this.filesService.uploadImageToS3( + `user-${userId}`, + pictureFile + ); - variables._set.picture = pictureUrl; + variables._set.picture = pictureUrl; + } } - const result = await this.userGraphql.updateUserProfile(token, variables); return result; - } catch (errors) { + } catch (error) { return { - errors, + errors: { + message: error.toString(), + }, }; } }