Compare commits

...

86 Commits

Author SHA1 Message Date
jamesgeorge007
a3e941271e chore: address lint errors 2024-03-07 12:24:33 +05:30
jamesgeorge007
e7fded89d5 refactor: organize imports 2024-03-07 12:24:33 +05:30
jamesgeorge007
40c8d7e4b7 feat: persist bulk mode line wrap setting for request variables
- Show line wrap toggle only if entries are present under http request headers & URL encoded params body.
- Organize imports.
2024-03-07 12:24:33 +05:30
nivedin
1d93d9dabe refactor: filter non empty value for env presidence 2024-03-07 12:24:33 +05:30
nivedin
e9f1dc7ba1 chore: code refactors 2024-03-07 12:24:33 +05:30
nivedin
1e906bbec3 fix: code refactor and handle secret var in req var 2024-03-07 12:24:33 +05:30
nivedin
7776668e40 chore: align reqvariable tab to end 2024-03-07 12:24:33 +05:30
nivedin
3d38b7678b chore: aligh button in error placeholder 2024-03-07 12:24:33 +05:30
nivedin
1ca215d887 chore: add parent container for req variable codemirror 2024-03-07 12:24:33 +05:30
nivedin
aec04a13e3 chore: diable context menu for empty text 2024-03-07 12:24:33 +05:30
nivedin
9a2058bbec chore: update env hover colours 2024-03-07 12:24:33 +05:30
nivedin
1c65147a77 fix: autocomplete env bug 2024-03-07 12:24:33 +05:30
nivedin
207eea538f chore: check for active request variables 2024-03-07 12:24:33 +05:30
nivedin
e30b59e1b5 refactor: update environments colour schema 2024-03-07 12:24:33 +05:30
nivedin
9704b3324b chore: update env autocomplete 2024-03-07 12:24:33 +05:30
nivedin
386bb453d4 refactor: update environment variables precedence order 2024-03-07 12:24:33 +05:30
nivedin
175641246e refactor: update components for embeds flow 2024-03-07 12:24:33 +05:30
nivedin
fb615d2d2b chore: add env autocomplete to components 2024-03-07 12:24:33 +05:30
nivedin
7685409ee6 chore: update env tooltip updation mechanism 2024-03-07 12:24:33 +05:30
nivedin
e01818444e chore: add env autocomplete in smartenvinput 2024-03-07 12:24:33 +05:30
nivedin
52220d9a2e chore: add action for inscpector and tooltip env 2024-03-07 12:24:33 +05:30
nivedin
82d687f665 refactor: update insomnia import 2024-03-07 12:24:33 +05:30
nivedin
d44083a380 refactor: update openapi import 2024-03-07 12:24:33 +05:30
nivedin
7589c57e86 refactor: update postman import for request variable 2024-03-07 12:24:33 +05:30
nivedin
c8009aec77 chore: update test cases in cli 2024-03-07 12:24:33 +05:30
nivedin
7114f4fd43 chore: update test cases in app 2024-03-07 12:24:33 +05:30
nivedin
ca1a4ec31b refactor: update codegen and curlparser 2024-03-07 12:24:33 +05:30
nivedin
359633102e refactor: add check for request variables for env inspector 2024-03-07 12:24:33 +05:30
nivedin
b278691a9d chore: update history 2024-03-07 12:24:33 +05:30
nivedin
c27e1be230 refactor: update env tooltip 2024-03-07 12:24:33 +05:30
nivedin
11e8ba7bb3 refactor: add request variables to request runner flow 2024-03-07 12:24:33 +05:30
nivedin
2a0000cfc4 chore: remove unused file 2024-03-07 12:24:33 +05:30
nivedin
0ed063724e feat: add request variable 2024-03-07 12:24:33 +05:30
nivedin
170ec15821 refactor: add v2 request schema 2024-03-07 12:24:33 +05:30
Mir Arif Hasan
3611cac241 feat(backend): sso callback url and scope added in infra-config (#3718) 2024-03-07 12:07:51 +05:30
Joel Jacob Stephen
919579b1da feat(sh-admin): introducing data analytics and newsletter configurations (#3845)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
Co-authored-by: nivedin <nivedinp@gmail.com>
2024-03-06 20:06:48 +05:30
Nivedin
4798d7bbbd refactor: remove restore tab popup and its functionalities (#3867) 2024-03-05 18:14:41 +05:30
Balu Babu
a0c6b22641 feat: full text search for TeamCollections and TeamRequests (#3857)
Co-authored-by: mirarifhasan <arif.ishan05@gmail.com>
2024-03-05 18:05:58 +05:30
James George
de8929ab18 feat(common): support simultaneous imports of collections and environment files (#3719) 2024-03-05 17:49:01 +05:30
Andrew Bastin
55a94bdccc chore: merge hoppscotch/release/2023.12.6 into hoppscotch/release/2024.3.0 2024-02-27 13:35:20 +05:30
Andrew Bastin
faab1d20fd chore: bump version to 2023.12.6 2024-02-26 22:31:58 +05:30
Anwarul Islam
bd406616ec fix: collection level authorization inheritance issue (#3852) 2024-02-23 19:39:55 +05:30
Andrew Bastin
6827e97ec5 refactor: possible links in email templates do not highlight (#3851) 2024-02-23 01:05:20 +05:30
amk-dev
10d2048975 fix: use x-www-form-urlencoded for token exchange requests 2024-02-22 00:43:50 +05:30
Nivedin
291f18591e fix: perfomance in safari (#3848) 2024-02-22 00:41:30 +05:30
James George
342532c9b1 fix(common): prevent exceptions with open shared requests in new tab action (#3835) 2024-02-22 00:36:45 +05:30
Balu Babu
cf039c482a feat: SH instance analytics data collection (#3838) 2024-02-22 00:35:12 +05:30
Mir Arif Hasan
ded2725116 feat: admin user management (backend) (#3786) 2024-02-21 21:35:08 +05:30
Balu Babu
9c6754c70f feat: inital setup info route (#3847) 2024-02-21 21:15:47 +05:30
James George
4bd54b12cd fix(persistence-service): add fallbacks for environments related schemas (#3832) 2024-02-15 23:38:56 +05:30
Andrew Bastin
ed6e9b6954 chore: bump version to 2023.12.5 2024-02-15 21:47:58 +05:30
James George
dfdd44b4ed fix(persistence-service): update global environment variables schema (#3829) 2024-02-15 21:40:31 +05:30
Akash K
fc34871dae fix: accessing undefined property variables (#3831) 2024-02-15 21:32:50 +05:30
Nivedin
45b532747e fix: environment tooltip update bug (#3819) 2024-02-13 17:42:02 +05:30
Akash K
de4635df23 chore: add workspace type property in request run analytics event (#3820)
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2024-02-13 17:38:11 +05:30
Nivedin
41bad1f3dc fix: secret environment flow bugs (#3817) 2024-02-10 20:22:10 +05:30
Andrew Bastin
ecca3d2032 chore: correct linting errors 2024-02-09 14:42:12 +05:30
Muhammed Ajmal M
47226be6d0 feat: persist line wrap setting (#3647)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-02-09 14:05:09 +05:30
Andrew Bastin
6a0e73fdec chore: bump versions 2024-02-08 22:41:59 +05:30
Andrew Bastin
672ee69b2c chore: correct linting errors 2024-02-08 22:33:03 +05:30
Joel Jacob Stephen
b359650d96 refactor: updated teams nomenclature in admin dashboard to workspaces (#3770) 2024-02-08 22:17:42 +05:30
James George
c0fae79678 fix(sh-admin): persist active selection in the sidebar (#3812) 2024-02-08 22:16:33 +05:30
James George
5bcc38e36b feat: support secret environment variables in CLI (#3815) 2024-02-08 22:08:18 +05:30
Nivedin
00862eb192 feat: secret variables in environments (#3779)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-02-08 21:58:42 +05:30
Akash K
16803acb26 chore: Oauth temporary ux improvements (#3792) 2024-02-06 20:35:29 +05:30
Nivedin
3911c9cd1f refactor: update share request flow (#3805) 2024-02-05 23:50:15 +05:30
Florian Metz
0028f6e878 feat(js-sandbox): expose atob & btoa functions for Node.js (#3724)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-02-05 23:12:55 +05:30
Anwarul Islam
0ba33ec187 fix: request endpoint heading (#3804) 2024-02-05 23:08:16 +05:30
James George
3482743782 chore(cli): emit bundle in ESM format (#3777) 2024-02-05 22:55:05 +05:30
James George
d7cdeb796a chore(common): analytics on spotlight (#3727)
Co-authored-by: amk-dev <akash.k.mohan98@gmail.com>
2024-02-02 15:32:06 +05:30
Joel Jacob Stephen
3d6adcc39d refactor: consolidated admin dashboard improvements (#3790)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-02-02 15:17:25 +05:30
Andrew Bastin
aab76f1358 chore: bump version to 2023.12.3 2024-01-30 20:27:25 +05:30
Anwarul Islam
a28a576c41 feat: team environment search and switch (#3700)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-01-30 19:49:04 +05:30
Liyas Thomas
0d0ad7a2f8 chore: added micro interactions (#3783)
Co-authored-by: Dmitry <mukovkin@yandex.ru>
2024-01-30 18:42:42 +05:30
Balu Babu
1df9de44b7 chore: upgraded prisma version to v5.8.0 (#3787) 2024-01-30 18:16:28 +05:30
Muhammed Ajmal M
4cba03e53f feat(js-sandbox): add pw.env.unset method (#3677)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-01-23 22:31:27 +05:30
Nivedin
9e1466a877 fix: bugs in shared request (#3704) 2024-01-23 22:24:18 +05:30
Anwarul Islam
b81ccb4ee3 fix: tab on current input field to focus the next input field (#3754) 2024-01-23 22:21:23 +05:30
Nivedin
27d0a7c437 refactor: persist running requests while switching tabs (#3742) 2024-01-23 22:13:57 +05:30
Nivedin
aca96dd5f2 refactor: add option to disable context menu (#3717) 2024-01-23 22:05:05 +05:30
Anwarul Islam
c0dbcc901f fix: documentation is not being generated on GQL (#3730)
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2024-01-23 22:00:41 +05:30
Joel Jacob Stephen
ba52c8cc37 refactor: improvements to the dashboard sidebar (#3709)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-01-23 21:55:42 +05:30
Dmitry
d1f6f40ef8 fix: perform logout if the silent refresh attempt fails (#3705)
Co-authored-by: Dmitry Mukovkin <d.mukovkin@cft.ru>
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
2024-01-23 21:53:59 +05:30
Akash K
99f5070f71 fix: unwanted double slashes when importing from openapi (#3745) 2024-01-23 21:49:34 +05:30
Andrew Bastin
cd371fc9d4 chore: bump versions to 2023.12.2 2024-01-03 16:58:51 +05:30
Jordi Been
59fef248c0 build: update node alpine version (#3660) 2024-01-03 16:49:51 +05:30
323 changed files with 12641 additions and 5352 deletions

View File

@@ -239,7 +239,7 @@ Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) f
📦 **Add-ons:** Official add-ons for hoppscotch. 📦 **Add-ons:** Official add-ons for hoppscotch.
- **[Hoppscotch CLI](https://github.com/hoppscotch/hopp-cli)** - Command-line interface for Hoppscotch. - **[Hoppscotch CLI](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-cli)** - Command-line interface for Hoppscotch.
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch. - **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch.
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience. - **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience.

View File

@@ -112,7 +112,7 @@ services:
build: build:
dockerfile: packages/hoppscotch-backend/Dockerfile dockerfile: packages/hoppscotch-backend/Dockerfile
context: . context: .
target: prod target: dev
env_file: env_file:
- ./.env - ./.env
restart: always restart: always
@@ -122,7 +122,7 @@ services:
- PORT=3000 - PORT=3000
volumes: volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target. # Uncomment the line below when modifying code. Only applicable when using the "dev" target.
# - ./packages/hoppscotch-backend/:/usr/src/app - ./packages/hoppscotch-backend/:/usr/src/app
- /usr/src/app/node_modules/ - /usr/src/app/node_modules/
depends_on: depends_on:
hoppscotch-db: hoppscotch-db:

View File

@@ -1,6 +1,6 @@
{ {
"name": "hoppscotch-backend", "name": "hoppscotch-backend",
"version": "2023.12.1", "version": "2023.12.6",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
@@ -34,12 +34,14 @@
"@nestjs/jwt": "^10.1.1", "@nestjs/jwt": "^10.1.1",
"@nestjs/passport": "^10.0.2", "@nestjs/passport": "^10.0.2",
"@nestjs/platform-express": "^10.2.6", "@nestjs/platform-express": "^10.2.6",
"@nestjs/schedule": "^4.0.1",
"@nestjs/throttler": "^5.0.0", "@nestjs/throttler": "^5.0.0",
"@prisma/client": "^4.16.2", "@prisma/client": "^5.8.0",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"cookie": "^0.5.0", "cookie": "^0.5.0",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"cron": "^3.1.6",
"express": "^4.17.1", "express": "^4.17.1",
"express-session": "^1.17.3", "express-session": "^1.17.3",
"fp-ts": "^2.13.1", "fp-ts": "^2.13.1",
@@ -57,7 +59,8 @@
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"passport-microsoft": "^1.0.0", "passport-microsoft": "^1.0.0",
"prisma": "^4.16.2", "posthog-node": "^3.6.3",
"prisma": "^5.8.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.6.0" "rxjs": "^7.6.0"

View File

@@ -0,0 +1,17 @@
-- AlterTable
ALTER TABLE
"TeamCollection"
ADD
titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- AlterTable
ALTER TABLE
"TeamRequest"
ADD
titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- CreateIndex
CREATE INDEX "TeamCollection_textSearch_idx" ON "TeamCollection" USING GIN (titleSearch);
-- CreateIndex
CREATE INDEX "TeamRequest_textSearch_idx" ON "TeamRequest" USING GIN (titleSearch);

View File

@@ -27,9 +27,7 @@ import {
} from './input-types.args'; } from './input-types.args';
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { User } from 'src/user/user.model'; import { UserDeletionResult } from 'src/user/user.model';
import { PaginationArgs } from 'src/types/input-types.args';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Admin) @Resolver(() => Admin)
@@ -49,203 +47,6 @@ export class AdminResolver {
return admin; return admin;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all admin users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async admins() {
const admins = await this.adminService.fetchAdmins();
return admins;
}
@ResolveField(() => User, {
description: 'Returns a user info by UID',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async userInfo(
@Args({
name: 'userUid',
type: () => ID,
description: 'The user UID',
})
userUid: string,
): Promise<AuthUser> {
const user = await this.adminService.fetchUserInfo(userUid);
if (E.isLeft(user)) throwErr(user.left);
return user.right;
}
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsers(args.cursor, args.take);
return users;
}
@ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users',
deprecationReason: 'Use `infra` query instead',
})
async invitedUsers(@Parent() admin: Admin): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers();
return users;
}
@ResolveField(() => [Team], {
description: 'Returns a list of all the teams in the infra',
deprecationReason: 'Use `infra` query instead',
})
async allTeams(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<Team[]> {
const teams = await this.adminService.fetchAllTeams(args.cursor, args.take);
return teams;
}
@ResolveField(() => Team, {
description: 'Returns a team info by ID when requested by Admin',
deprecationReason: 'Use `infra` query instead',
})
async teamInfo(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which info to fetch',
})
teamID: string,
): Promise<Team> {
const team = await this.adminService.getTeamInfo(teamID);
if (E.isLeft(team)) throwErr(team.left);
return team.right;
}
@ResolveField(() => Number, {
description: 'Return count of all the members in a team',
deprecationReason: 'Use `infra` query instead',
})
async membersCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
nullable: false,
})
teamID: string,
): Promise<number> {
const teamMembersCount = await this.adminService.membersCountInTeam(teamID);
return teamMembersCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored collections in a team',
deprecationReason: 'Use `infra` query instead',
})
async collectionCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamCollCount = await this.adminService.collectionCountInTeam(teamID);
return teamCollCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored requests in a team',
deprecationReason: 'Use `infra` query instead',
})
async requestCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamReqCount = await this.adminService.requestCountInTeam(teamID);
return teamReqCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored environments in a team',
deprecationReason: 'Use `infra` query instead',
})
async environmentCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const envsCount = await this.adminService.environmentCountInTeam(teamID);
return envsCount;
}
@ResolveField(() => [TeamInvitation], {
description: 'Return all the pending invitations in a team',
deprecationReason: 'Use `infra` query instead',
})
async pendingInvitationCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
) {
const invitations = await this.adminService.pendingInvitationCountInTeam(
teamID,
);
return invitations;
}
@ResolveField(() => Number, {
description: 'Return total number of Users in organization',
deprecationReason: 'Use `infra` query instead',
})
async usersCount() {
return this.adminService.getUsersCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Teams in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamsCount() {
return this.adminService.getTeamsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Collections in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamCollectionsCount() {
return this.adminService.getTeamCollectionsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Requests in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamRequestsCount() {
return this.adminService.getTeamRequestsCount();
}
/* Mutations */ /* Mutations */
@Mutation(() => InvitedUser, { @Mutation(() => InvitedUser, {
@@ -269,8 +70,26 @@ export class AdminResolver {
return invitedUser.right; return invitedUser.right;
} }
@Mutation(() => Boolean, {
description: 'Revoke a user invites by invitee emails',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async revokeUserInvitationsByAdmin(
@Args({
name: 'inviteeEmails',
description: 'Invitee Emails',
type: () => [String],
})
inviteeEmails: string[],
): Promise<boolean> {
const invite = await this.adminService.revokeUserInvitations(inviteeEmails);
if (E.isLeft(invite)) throwErr(invite.left);
return invite.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Delete an user account from infra', description: 'Delete an user account from infra',
deprecationReason: 'Use removeUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserByAdmin( async removeUserByAdmin(
@@ -281,12 +100,33 @@ export class AdminResolver {
}) })
userUID: string, userUID: string,
): Promise<boolean> { ): Promise<boolean> {
const invitedUser = await this.adminService.removeUserAccount(userUID); const removedUser = await this.adminService.removeUserAccount(userUID);
if (E.isLeft(invitedUser)) throwErr(invitedUser.left); if (E.isLeft(removedUser)) throwErr(removedUser.left);
return invitedUser.right; return removedUser.right;
} }
@Mutation(() => [UserDeletionResult], {
description: 'Delete user accounts from infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<UserDeletionResult[]> {
const deletionResults = await this.adminService.removeUserAccounts(
userUIDs,
);
if (E.isLeft(deletionResults)) throwErr(deletionResults.left);
return deletionResults.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Make user an admin', description: 'Make user an admin',
deprecationReason: 'Use makeUsersAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUserAdmin( async makeUserAdmin(
@@ -302,8 +142,51 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Make users an admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUsersAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.makeUsersAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, {
description: 'Update user display name',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async updateUserDisplayNameByAdmin(
@Args({
name: 'userUID',
description: 'users UID',
type: () => ID,
})
userUID: string,
@Args({
name: 'displayName',
description: 'users display name',
})
displayName: string,
): Promise<boolean> {
const isUpdated = await this.adminService.updateUserDisplayName(
userUID,
displayName,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Remove user as admin', description: 'Remove user as admin',
deprecationReason: 'Use demoteUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserAsAdmin( async removeUserAsAdmin(
@@ -319,6 +202,23 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Remove users as admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async demoteUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.demoteUsersByAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Team, { @Mutation(() => Team, {
description: description:
'Create a new team by providing the user uid to nominate as Team owner', 'Create a new team by providing the user uid to nominate as Team owner',

View File

@@ -1,7 +1,7 @@
import { AdminService } from './admin.service'; import { AdminService } from './admin.service';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { mockDeep } from 'jest-mock-extended'; import { mockDeep } from 'jest-mock-extended';
import { InvitedUsers } from '@prisma/client'; import { InvitedUsers, User as DbUser } from '@prisma/client';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
import { TeamService } from '../team/team.service'; import { TeamService } from '../team/team.service';
import { TeamEnvironmentsService } from '../team-environments/team-environments.service'; import { TeamEnvironmentsService } from '../team-environments/team-environments.service';
@@ -13,10 +13,15 @@ import { PrismaService } from 'src/prisma/prisma.service';
import { import {
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_NOT_FOUND,
} from '../errors'; } from '../errors';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import * as E from 'fp-ts/Either';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockPubSub = mockDeep<PubSubService>(); const mockPubSub = mockDeep<PubSubService>();
@@ -58,20 +63,87 @@ const invitedUsers: InvitedUsers[] = [
invitedOn: new Date(), invitedOn: new Date(),
}, },
]; ];
const dbAdminUsers: DbUser[] = [
{
uid: 'uid 1',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
},
{
uid: 'uid 2',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
},
];
const dbNonAminUser: DbUser = {
uid: 'uid 3',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: false,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
};
describe('AdminService', () => { describe('AdminService', () => {
describe('fetchInvitedUsers', () => { describe('fetchInvitedUsers', () => {
test('should resolve right and return an array of invited users', async () => { test('should resolve right and apply pagination correctly', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers); mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const results = await adminService.fetchInvitedUsers(); const paginationArgs: OffsetPaginationArgs = { take: 5, skip: 2 };
const results = await adminService.fetchInvitedUsers(paginationArgs);
expect(mockPrisma.invitedUsers.findMany).toHaveBeenCalledWith({
...paginationArgs,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: [dbAdminUsers[0].email],
},
},
},
});
});
test('should resolve right and return an array of invited users', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const results = await adminService.fetchInvitedUsers(paginationArgs);
expect(results).toEqual(invitedUsers); expect(results).toEqual(invitedUsers);
}); });
test('should resolve left and return an empty array if invited users not found', async () => { test('should resolve left and return an empty array if invited users not found', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
mockPrisma.invitedUsers.findMany.mockResolvedValue([]); mockPrisma.invitedUsers.findMany.mockResolvedValue([]);
const results = await adminService.fetchInvitedUsers(); const results = await adminService.fetchInvitedUsers(paginationArgs);
expect(results).toEqual([]); expect(results).toEqual([]);
}); });
}); });
@@ -134,6 +206,58 @@ describe('AdminService', () => {
}); });
}); });
describe('revokeUserInvitations', () => {
test('should resolve left and return error if email not invited', async () => {
mockPrisma.invitedUsers.deleteMany.mockRejectedValueOnce(
'RecordNotFound',
);
const result = await adminService.revokeUserInvitations([
'test@gmail.com',
]);
expect(result).toEqualLeft(USER_INVITATION_DELETION_FAILED);
});
test('should resolve right and return deleted invitee email', async () => {
const adminUid = 'adminUid';
mockPrisma.invitedUsers.deleteMany.mockResolvedValueOnce({ count: 1 });
const result = await adminService.revokeUserInvitations([
invitedUsers[0].inviteeEmail,
]);
expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({
where: {
inviteeEmail: { in: [invitedUsers[0].inviteeEmail] },
},
});
expect(result).toEqualRight(true);
});
});
describe('removeUsersAsAdmin', () => {
test('should resolve right and make admins to users', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin([dbAdminUsers[0].uid]),
).toEqualRight(true);
});
test('should resolve left and return error if only one admin in the infra', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin(
dbAdminUsers.map((user) => user.uid),
),
).toEqualLeft(ONLY_ONE_ADMIN_ACCOUNT);
});
});
describe('getUsersCount', () => { describe('getUsersCount', () => {
test('should return count of all users in the organization', async () => { test('should return count of all users in the organization', async () => {
mockUserService.getUsersCount.mockResolvedValueOnce(10); mockUserService.getUsersCount.mockResolvedValueOnce(10);

View File

@@ -6,13 +6,16 @@ import * as E from 'fp-ts/Either';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { validateEmail } from '../utils'; import { validateEmail } from '../utils';
import { import {
ADMIN_CAN_NOT_BE_DELETED,
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
EMAIL_FAILED, EMAIL_FAILED,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT, ONLY_ONE_ADMIN_ACCOUNT,
TEAM_INVITE_ALREADY_MEMBER, TEAM_INVITE_ALREADY_MEMBER,
TEAM_INVITE_NO_INVITE_FOUND, TEAM_INVITE_NO_INVITE_FOUND,
USERS_NOT_FOUND,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_IS_ADMIN, USER_IS_ADMIN,
USER_NOT_FOUND, USER_NOT_FOUND,
} from '../errors'; } from '../errors';
@@ -26,6 +29,8 @@ import { TeamInvitationService } from '../team-invitation/team-invitation.servic
import { TeamMemberRole } from '../team/team.model'; import { TeamMemberRole } from '../team/team.model';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import { UserDeletionResult } from 'src/user/user.model';
@Injectable() @Injectable()
export class AdminService { export class AdminService {
@@ -48,12 +53,30 @@ export class AdminService {
* @param cursorID Users uid * @param cursorID Users uid
* @param take number of users to fetch * @param take number of users to fetch
* @returns an Either of array of user or error * @returns an Either of array of user or error
* @deprecated use fetchUsersV2 instead
*/ */
async fetchUsers(cursorID: string, take: number) { async fetchUsers(cursorID: string, take: number) {
const allUsers = await this.userService.fetchAllUsers(cursorID, take); const allUsers = await this.userService.fetchAllUsers(cursorID, take);
return allUsers; return allUsers;
} }
/**
* Fetch all the users in the infra.
* @param searchString search on users displayName or email
* @param paginationOption pagination options
* @returns an Either of array of user or error
*/
async fetchUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const allUsers = await this.userService.fetchAllUsersV2(
searchString,
paginationOption,
);
return allUsers;
}
/** /**
* Invite a user to join the infra. * Invite a user to join the infra.
* @param adminUID Admin's UID * @param adminUID Admin's UID
@@ -110,14 +133,68 @@ export class AdminService {
return E.right(invitedUser); return E.right(invitedUser);
} }
/**
* Update the display name of a user
* @param userUid Who's display name is being updated
* @param displayName New display name of the user
* @returns an Either of boolean or error
*/
async updateUserDisplayName(userUid: string, displayName: string) {
const updatedUser = await this.userService.updateUserDisplayName(
userUid,
displayName,
);
if (E.isLeft(updatedUser)) return E.left(updatedUser.left);
return E.right(true);
}
/**
* Revoke infra level user invitations
* @param inviteeEmails Invitee's emails
* @param adminUid Admin Uid
* @returns an Either of boolean or error string
*/
async revokeUserInvitations(inviteeEmails: string[]) {
try {
await this.prisma.invitedUsers.deleteMany({
where: {
inviteeEmail: { in: inviteeEmails },
},
});
return E.right(true);
} catch (error) {
return E.left(USER_INVITATION_DELETION_FAILED);
}
}
/** /**
* Fetch the list of invited users by the admin. * Fetch the list of invited users by the admin.
* @returns an Either of array of `InvitedUser` object or error * @returns an Either of array of `InvitedUser` object or error
*/ */
async fetchInvitedUsers() { async fetchInvitedUsers(paginationOption: OffsetPaginationArgs) {
const invitedUsers = await this.prisma.invitedUsers.findMany(); const userEmailObjs = await this.prisma.user.findMany({
select: {
email: true,
},
});
const users: InvitedUser[] = invitedUsers.map( const pendingInvitedUsers = await this.prisma.invitedUsers.findMany({
take: paginationOption.take,
skip: paginationOption.skip,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: userEmailObjs.map((user) => user.email),
},
},
},
});
const users: InvitedUser[] = pendingInvitedUsers.map(
(user) => <InvitedUser>{ ...user }, (user) => <InvitedUser>{ ...user },
); );
@@ -337,6 +414,7 @@ export class AdminService {
* Remove a user account by UID * Remove a user account by UID
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use removeUserAccounts instead
*/ */
async removeUserAccount(userUid: string) { async removeUserAccount(userUid: string) {
const user = await this.userService.findUserById(userUid); const user = await this.userService.findUserById(userUid);
@@ -349,10 +427,73 @@ export class AdminService {
return E.right(delUser.right); return E.right(delUser.right);
} }
/**
* Remove user (not Admin) accounts by UIDs
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async removeUserAccounts(userUIDs: string[]) {
const userDeleteResult: UserDeletionResult[] = [];
// step 1: fetch all users
const allUsersList = await this.userService.findUsersByIds(userUIDs);
if (allUsersList.length === 0) return E.left(USERS_NOT_FOUND);
// step 2: admin user can not be deleted without removing admin status/role
allUsersList.forEach((user) => {
if (user.isAdmin) {
userDeleteResult.push({
userUID: user.uid,
isDeleted: false,
errorMessage: ADMIN_CAN_NOT_BE_DELETED,
});
}
});
const nonAdminUsers = allUsersList.filter((user) => !user.isAdmin);
let deletedUserEmails: string[] = [];
// step 3: delete non-admin users
const deletionPromises = nonAdminUsers.map((user) => {
return this.userService
.deleteUserByUID(user)()
.then((res) => {
if (E.isLeft(res)) {
return {
userUID: user.uid,
isDeleted: false,
errorMessage: res.left,
} as UserDeletionResult;
}
deletedUserEmails.push(user.email);
return {
userUID: user.uid,
isDeleted: true,
errorMessage: null,
} as UserDeletionResult;
});
});
const promiseResult = await Promise.allSettled(deletionPromises);
// step 4: revoke all the invites sent to the deleted users
await this.revokeUserInvitations(deletedUserEmails);
// step 5: return the result
promiseResult.forEach((result) => {
if (result.status === 'fulfilled') {
userDeleteResult.push(result.value);
}
});
return E.right(userDeleteResult);
}
/** /**
* Make a user an admin * Make a user an admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use makeUsersAdmin instead
*/ */
async makeUserAdmin(userUID: string) { async makeUserAdmin(userUID: string) {
const admin = await this.userService.makeAdmin(userUID); const admin = await this.userService.makeAdmin(userUID);
@@ -360,10 +501,22 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Make users to admin
* @param userUid User UIDs
* @returns an Either of boolean or error
*/
async makeUsersAdmin(userUIDs: string[]) {
const isUpdated = await this.userService.makeAdmins(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(true);
}
/** /**
* Remove user as admin * Remove user as admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use demoteUsersByAdmin instead
*/ */
async removeUserAsAdmin(userUID: string) { async removeUserAsAdmin(userUID: string) {
const adminUsers = await this.userService.fetchAdminUsers(); const adminUsers = await this.userService.fetchAdminUsers();
@@ -374,6 +527,26 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Remove users as admin
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async demoteUsersByAdmin(userUIDs: string[]) {
const adminUsers = await this.userService.fetchAdminUsers();
const remainingAdmins = adminUsers.filter(
(adminUser) => !userUIDs.includes(adminUser.uid),
);
if (remainingAdmins.length < 1) {
return E.left(ONLY_ONE_ADMIN_ACCOUNT);
}
const isUpdated = await this.userService.removeUsersAsAdmin(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(isUpdated.right);
}
/** /**
* Fetch list of all the Users in org * Fetch list of all the Users in org
* @returns number of users in the org * @returns number of users in the org

View File

@@ -0,0 +1,11 @@
import { Injectable, ExecutionContext, CanActivate } from '@nestjs/common';
@Injectable()
export class RESTAdminGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const user = request.user;
return user.isAdmin;
}
}

View File

@@ -17,7 +17,10 @@ import { AuthUser } from 'src/types/AuthUser';
import { throwErr } from 'src/utils'; import { throwErr } from 'src/utils';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { Admin } from './admin.model'; import { Admin } from './admin.model';
import { PaginationArgs } from 'src/types/input-types.args'; import {
OffsetPaginationArgs,
PaginationArgs,
} from 'src/types/input-types.args';
import { InvitedUser } from './invited-user.model'; import { InvitedUser } from './invited-user.model';
import { Team } from 'src/team/team.model'; import { Team } from 'src/team/team.model';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
@@ -29,7 +32,8 @@ import {
EnableAndDisableSSOArgs, EnableAndDisableSSOArgs,
InfraConfigArgs, InfraConfigArgs,
} from 'src/infra-config/input-args'; } from 'src/infra-config/input-args';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig'; import { InfraConfigEnum } from 'src/types/InfraConfig';
import { ServiceStatus } from 'src/infra-config/helper';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Infra) @Resolver(() => Infra)
@@ -76,6 +80,7 @@ export class InfraResolver {
@ResolveField(() => [User], { @ResolveField(() => [User], {
description: 'Returns a list of all the users in infra', description: 'Returns a list of all the users in infra',
deprecationReason: 'Use allUsersV2 instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> { async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> {
@@ -83,11 +88,33 @@ export class InfraResolver {
return users; return users;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsersV2(
@Args({
name: 'searchString',
nullable: true,
description: 'Search on users displayName or email',
})
searchString: string,
@Args() paginationOption: OffsetPaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsersV2(
searchString,
paginationOption,
);
return users;
}
@ResolveField(() => [InvitedUser], { @ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users', description: 'Returns a list of all the invited users',
}) })
async invitedUsers(): Promise<InvitedUser[]> { async invitedUsers(
const users = await this.adminService.fetchInvitedUsers(); @Args() args: OffsetPaginationArgs,
): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers(args);
return users; return users;
} }
@@ -247,10 +274,10 @@ export class InfraResolver {
async infraConfigs( async infraConfigs(
@Args({ @Args({
name: 'configNames', name: 'configNames',
type: () => [InfraConfigEnumForClient], type: () => [InfraConfigEnum],
description: 'Configs to fetch', description: 'Configs to fetch',
}) })
names: InfraConfigEnumForClient[], names: InfraConfigEnum[],
) { ) {
const infraConfigs = await this.infraConfigService.getMany(names); const infraConfigs = await this.infraConfigService.getMany(names);
if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left); if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left);
@@ -284,6 +311,25 @@ export class InfraResolver {
return updatedRes.right; return updatedRes.right;
} }
@Mutation(() => Boolean, {
description: 'Enable or disable analytics collection',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async toggleAnalyticsCollection(
@Args({
name: 'status',
type: () => ServiceStatus,
description: 'Toggle analytics collection',
})
analyticsCollectionStatus: ServiceStatus,
) {
const res = await this.infraConfigService.toggleAnalyticsCollection(
analyticsCollectionStatus,
);
if (E.isLeft(res)) throwErr(res.left);
return res.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Reset Infra Configs with default values (.env)', description: 'Reset Infra Configs with default values (.env)',
}) })
@@ -306,7 +352,9 @@ export class InfraResolver {
}) })
providerInfo: EnableAndDisableSSOArgs[], providerInfo: EnableAndDisableSSOArgs[],
) { ) {
const isUpdated = await this.infraConfigService.enableAndDisableSSO(providerInfo); const isUpdated = await this.infraConfigService.enableAndDisableSSO(
providerInfo,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left); if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return true; return true;

View File

@@ -24,6 +24,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { InfraConfigModule } from './infra-config/infra-config.module'; import { InfraConfigModule } from './infra-config/infra-config.module';
import { loadInfraConfiguration } from './infra-config/helper'; import { loadInfraConfiguration } from './infra-config/helper';
import { MailerModule } from './mailer/mailer.module'; import { MailerModule } from './mailer/mailer.module';
import { PosthogModule } from './posthog/posthog.module';
import { ScheduleModule } from '@nestjs/schedule';
@Module({ @Module({
imports: [ imports: [
@@ -96,6 +98,8 @@ import { MailerModule } from './mailer/mailer.module';
UserCollectionModule, UserCollectionModule,
ShortcodeModule, ShortcodeModule,
InfraConfigModule, InfraConfigModule,
PosthogModule,
ScheduleModule.forRoot(),
], ],
providers: [GQLComplexityPlugin], providers: [GQLComplexityPlugin],
controllers: [AppController], controllers: [AppController],

View File

@@ -18,12 +18,7 @@ import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { GqlUser } from 'src/decorators/gql-user.decorator'; import { GqlUser } from 'src/decorators/gql-user.decorator';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { RTCookie } from 'src/decorators/rt-cookie.decorator'; import { RTCookie } from 'src/decorators/rt-cookie.decorator';
import { import { AuthProvider, authCookieHandler, authProviderCheck } from './helper';
AuthProvider,
authCookieHandler,
authProviderCheck,
throwHTTPErr,
} from './helper';
import { GoogleSSOGuard } from './guards/google-sso.guard'; import { GoogleSSOGuard } from './guards/google-sso.guard';
import { GithubSSOGuard } from './guards/github-sso.guard'; import { GithubSSOGuard } from './guards/github-sso.guard';
import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard'; import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard';
@@ -31,6 +26,7 @@ import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.gua
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard) @UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'auth', version: '1' }) @Controller({ path: 'auth', version: '1' })

View File

@@ -12,7 +12,10 @@ import { GithubStrategy } from './strategies/github.strategy';
import { MicrosoftStrategy } from './strategies/microsoft.strategy'; import { MicrosoftStrategy } from './strategies/microsoft.strategy';
import { AuthProvider, authProviderCheck } from './helper'; import { AuthProvider, authProviderCheck } from './helper';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { loadInfraConfiguration } from 'src/infra-config/helper'; import {
isInfraConfigTablePopulated,
loadInfraConfiguration,
} from 'src/infra-config/helper';
import { InfraConfigModule } from 'src/infra-config/infra-config.module'; import { InfraConfigModule } from 'src/infra-config/infra-config.module';
@Module({ @Module({
@@ -34,6 +37,11 @@ import { InfraConfigModule } from 'src/infra-config/infra-config.module';
}) })
export class AuthModule { export class AuthModule {
static async register() { static async register() {
const isInfraConfigPopulated = await isInfraConfigTablePopulated();
if (!isInfraConfigPopulated) {
return { module: AuthModule };
}
const env = await loadInfraConfiguration(); const env = await loadInfraConfiguration();
const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS; const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS;

View File

@@ -24,7 +24,7 @@ import {
RefreshTokenPayload, RefreshTokenPayload,
} from 'src/types/AuthTokens'; } from 'src/types/AuthTokens';
import { JwtService } from '@nestjs/jwt'; import { JwtService } from '@nestjs/jwt';
import { AuthError } from 'src/types/AuthError'; import { RESTError } from 'src/types/RESTError';
import { AuthUser, IsAdmin } from 'src/types/AuthUser'; import { AuthUser, IsAdmin } from 'src/types/AuthUser';
import { VerificationToken } from '@prisma/client'; import { VerificationToken } from '@prisma/client';
import { Origin } from './helper'; import { Origin } from './helper';
@@ -117,7 +117,7 @@ export class AuthService {
userUid, userUid,
); );
if (E.isLeft(updatedUser)) if (E.isLeft(updatedUser))
return E.left(<AuthError>{ return E.left(<RESTError>{
message: updatedUser.left, message: updatedUser.left,
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
}); });
@@ -255,7 +255,7 @@ export class AuthService {
*/ */
async verifyMagicLinkTokens( async verifyMagicLinkTokens(
magicLinkIDTokens: VerifyMagicDto, magicLinkIDTokens: VerifyMagicDto,
): Promise<E.Right<AuthTokens> | E.Left<AuthError>> { ): Promise<E.Right<AuthTokens> | E.Left<RESTError>> {
const passwordlessTokens = await this.validatePasswordlessTokens( const passwordlessTokens = await this.validatePasswordlessTokens(
magicLinkIDTokens, magicLinkIDTokens,
); );
@@ -373,7 +373,7 @@ export class AuthService {
if (usersCount === 1) { if (usersCount === 1) {
const elevatedUser = await this.usersService.makeAdmin(user.uid); const elevatedUser = await this.usersService.makeAdmin(user.uid);
if (E.isLeft(elevatedUser)) if (E.isLeft(elevatedUser))
return E.left(<AuthError>{ return E.left(<RESTError>{
message: elevatedUser.left, message: elevatedUser.left,
statusCode: HttpStatus.NOT_FOUND, statusCode: HttpStatus.NOT_FOUND,
}); });

View File

@@ -1,9 +1,10 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class GithubSSOGuard extends AuthGuard('github') implements CanActivate { export class GithubSSOGuard extends AuthGuard('github') implements CanActivate {

View File

@@ -1,9 +1,10 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate { export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate {

View File

@@ -1,9 +1,10 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { throwHTTPErr } from 'src/utils';
@Injectable() @Injectable()
export class MicrosoftSSOGuard export class MicrosoftSSOGuard

View File

@@ -1,6 +1,5 @@
import { HttpException, HttpStatus } from '@nestjs/common'; import { HttpException, HttpStatus } from '@nestjs/common';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { AuthError } from 'src/types/AuthError';
import { AuthTokens } from 'src/types/AuthTokens'; import { AuthTokens } from 'src/types/AuthTokens';
import { Response } from 'express'; import { Response } from 'express';
import * as cookie from 'cookie'; import * as cookie from 'cookie';
@@ -25,15 +24,6 @@ export enum AuthProvider {
EMAIL = 'EMAIL', EMAIL = 'EMAIL',
} }
/**
* This function allows throw to be used as an expression
* @param errMessage Message present in the error message
*/
export function throwHTTPErr(errorData: AuthError): never {
const { message, statusCode } = errorData;
throw new HttpException(message, statusCode);
}
/** /**
* Sets and returns the cookies in the response object on successful authentication * Sets and returns the cookies in the response object on successful authentication
* @param res Express Response Object * @param res Express Response Object

View File

@@ -17,8 +17,8 @@ export class GithubStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.GITHUB_CLIENT_ID'), clientID: configService.get('INFRA.GITHUB_CLIENT_ID'),
clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'), clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'),
callbackURL: configService.get('GITHUB_CALLBACK_URL'), callbackURL: configService.get('INFRA.GITHUB_CALLBACK_URL'),
scope: [configService.get('GITHUB_SCOPE')], scope: [configService.get('INFRA.GITHUB_SCOPE')],
store: true, store: true,
}); });
} }

View File

@@ -17,8 +17,8 @@ export class GoogleStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'), clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'),
clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'), clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'),
callbackURL: configService.get('GOOGLE_CALLBACK_URL'), callbackURL: configService.get('INFRA.GOOGLE_CALLBACK_URL'),
scope: configService.get('GOOGLE_SCOPE').split(','), scope: configService.get('INFRA.GOOGLE_SCOPE').split(','),
passReqToCallback: true, passReqToCallback: true,
store: true, store: true,
}); });

View File

@@ -17,9 +17,9 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy) {
super({ super({
clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'), clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'),
clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'), clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'),
callbackURL: configService.get('MICROSOFT_CALLBACK_URL'), callbackURL: configService.get('INFRA.MICROSOFT_CALLBACK_URL'),
scope: [configService.get('MICROSOFT_SCOPE')], scope: [configService.get('INFRA.MICROSOFT_SCOPE')],
tenant: configService.get('MICROSOFT_TENANT'), tenant: configService.get('INFRA.MICROSOFT_TENANT'),
store: true, store: true,
}); });
} }

View File

@@ -10,6 +10,14 @@ export const DUPLICATE_EMAIL = 'email/both_emails_cannot_be_same' as const;
export const ONLY_ONE_ADMIN_ACCOUNT = export const ONLY_ONE_ADMIN_ACCOUNT =
'admin/only_one_admin_account_found' as const; 'admin/only_one_admin_account_found' as const;
/**
* Admin user can not be deleted
* To delete the admin user, first make the Admin user a normal user
* (AdminService)
*/
export const ADMIN_CAN_NOT_BE_DELETED =
'admin/admin_can_not_be_deleted' as const;
/** /**
* Token Authorization failed (Check 'Authorization' Header) * Token Authorization failed (Check 'Authorization' Header)
* (GqlAuthGuard) * (GqlAuthGuard)
@@ -99,6 +107,13 @@ export const USER_IS_OWNER = 'user/is_owner' as const;
*/ */
export const USER_IS_ADMIN = 'user/is_admin' as const; export const USER_IS_ADMIN = 'user/is_admin' as const;
/**
* User invite deletion failure error due to invitation not found
* (AdminService)
*/
export const USER_INVITATION_DELETION_FAILED =
'user/invitation_deletion_failed' as const;
/** /**
* Teams not found * Teams not found
* (TeamsService) * (TeamsService)
@@ -213,6 +228,12 @@ export const TEAM_COL_NOT_SAME_PARENT =
export const TEAM_COL_SAME_NEXT_COLL = export const TEAM_COL_SAME_NEXT_COLL =
'team_coll/collection_and_next_collection_are_same'; 'team_coll/collection_and_next_collection_are_same';
/**
* Team Collection search failed
* (TeamCollectionService)
*/
export const TEAM_COL_SEARCH_FAILED = 'team_coll/team_collection_search_failed';
/** /**
* Team Collection Re-Ordering Failed * Team Collection Re-Ordering Failed
* (TeamCollectionService) * (TeamCollectionService)
@@ -268,6 +289,13 @@ export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
export const TEAM_COLL_DATA_INVALID = export const TEAM_COLL_DATA_INVALID =
'team_coll/team_coll_data_invalid' as const; 'team_coll/team_coll_data_invalid' as const;
/**
* Team Collection parent tree generation failed
* (TeamCollectionService)
*/
export const TEAM_COLL_PARENT_TREE_GEN_FAILED =
'team_coll/team_coll_parent_tree_generation_failed';
/** /**
* Tried to perform an action on a request that doesn't accept their member role level * Tried to perform an action on a request that doesn't accept their member role level
* (GqlRequestTeamMemberGuard) * (GqlRequestTeamMemberGuard)
@@ -293,6 +321,19 @@ export const TEAM_REQ_INVALID_TARGET_COLL_ID =
*/ */
export const TEAM_REQ_REORDERING_FAILED = 'team_req/reordering_failed' as const; export const TEAM_REQ_REORDERING_FAILED = 'team_req/reordering_failed' as const;
/**
* Team Request search failed
* (TeamRequestService)
*/
export const TEAM_REQ_SEARCH_FAILED = 'team_req/team_request_search_failed';
/**
* Team Request parent tree generation failed
* (TeamRequestService)
*/
export const TEAM_REQ_PARENT_TREE_GEN_FAILED =
'team_req/team_req_parent_tree_generation_failed';
/** /**
* No Postmark Sender Email defined * No Postmark Sender Email defined
* (AuthService) * (AuthService)
@@ -690,9 +731,22 @@ export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const;
export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED = export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED =
'infra_config/service_not_configured' as const; 'infra_config/service_not_configured' as const;
/**
* Infra Config update/fetch operation not allowed
* (InfraConfigService)
*/
export const INFRA_CONFIG_OPERATION_NOT_ALLOWED =
'infra_config/operation_not_allowed';
/** /**
* Error message for when the database table does not exist * Error message for when the database table does not exist
* (InfraConfigService) * (InfraConfigService)
*/ */
export const DATABASE_TABLE_NOT_EXIST = export const DATABASE_TABLE_NOT_EXIST =
'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations'; 'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations';
/**
* PostHog client is not initialized
* (InfraConfigService)
*/
export const POSTHOG_CLIENT_NOT_INITIALIZED = 'posthog/client_not_initialized';

View File

@@ -1,8 +1,12 @@
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
import { AUTH_PROVIDER_NOT_CONFIGURED } from 'src/errors'; import {
AUTH_PROVIDER_NOT_CONFIGURED,
DATABASE_TABLE_NOT_EXIST,
} from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigEnum } from 'src/types/InfraConfig'; import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwErr } from 'src/utils'; import { throwErr } from 'src/utils';
import { randomBytes } from 'crypto';
export enum ServiceStatus { export enum ServiceStatus {
ENABLE = 'ENABLE', ENABLE = 'ENABLE',
@@ -13,14 +17,21 @@ const AuthProviderConfigurations = {
[AuthProvider.GOOGLE]: [ [AuthProvider.GOOGLE]: [
InfraConfigEnum.GOOGLE_CLIENT_ID, InfraConfigEnum.GOOGLE_CLIENT_ID,
InfraConfigEnum.GOOGLE_CLIENT_SECRET, InfraConfigEnum.GOOGLE_CLIENT_SECRET,
InfraConfigEnum.GOOGLE_CALLBACK_URL,
InfraConfigEnum.GOOGLE_SCOPE,
], ],
[AuthProvider.GITHUB]: [ [AuthProvider.GITHUB]: [
InfraConfigEnum.GITHUB_CLIENT_ID, InfraConfigEnum.GITHUB_CLIENT_ID,
InfraConfigEnum.GITHUB_CLIENT_SECRET, InfraConfigEnum.GITHUB_CLIENT_SECRET,
InfraConfigEnum.GITHUB_CALLBACK_URL,
InfraConfigEnum.GITHUB_SCOPE,
], ],
[AuthProvider.MICROSOFT]: [ [AuthProvider.MICROSOFT]: [
InfraConfigEnum.MICROSOFT_CLIENT_ID, InfraConfigEnum.MICROSOFT_CLIENT_ID,
InfraConfigEnum.MICROSOFT_CLIENT_SECRET, InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
InfraConfigEnum.MICROSOFT_CALLBACK_URL,
InfraConfigEnum.MICROSOFT_SCOPE,
InfraConfigEnum.MICROSOFT_TENANT,
], ],
[AuthProvider.EMAIL]: [ [AuthProvider.EMAIL]: [
InfraConfigEnum.MAILER_SMTP_URL, InfraConfigEnum.MAILER_SMTP_URL,
@@ -53,6 +64,125 @@ export async function loadInfraConfiguration() {
} }
} }
/**
* Read the default values from .env file and return them as an array
* @returns Array of default infra configs
*/
export async function getDefaultInfraConfigs(): Promise<
{ name: InfraConfigEnum; value: string }[]
> {
const prisma = new PrismaService();
// Prepare rows for 'infra_config' table with default values (from .env) for each 'name'
const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [
{
name: InfraConfigEnum.MAILER_SMTP_URL,
value: process.env.MAILER_SMTP_URL,
},
{
name: InfraConfigEnum.MAILER_ADDRESS_FROM,
value: process.env.MAILER_ADDRESS_FROM,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: process.env.GOOGLE_CLIENT_ID,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_SECRET,
value: process.env.GOOGLE_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GOOGLE_CALLBACK_URL,
value: process.env.GOOGLE_CALLBACK_URL,
},
{
name: InfraConfigEnum.GOOGLE_SCOPE,
value: process.env.GOOGLE_SCOPE,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_ID,
value: process.env.GITHUB_CLIENT_ID,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_SECRET,
value: process.env.GITHUB_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GITHUB_CALLBACK_URL,
value: process.env.GITHUB_CALLBACK_URL,
},
{
name: InfraConfigEnum.GITHUB_SCOPE,
value: process.env.GITHUB_SCOPE,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_ID,
value: process.env.MICROSOFT_CLIENT_ID,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
value: process.env.MICROSOFT_CLIENT_SECRET,
},
{
name: InfraConfigEnum.MICROSOFT_CALLBACK_URL,
value: process.env.MICROSOFT_CALLBACK_URL,
},
{
name: InfraConfigEnum.MICROSOFT_SCOPE,
value: process.env.MICROSOFT_SCOPE,
},
{
name: InfraConfigEnum.MICROSOFT_TENANT,
value: process.env.MICROSOFT_TENANT,
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: getConfiguredSSOProviders(),
},
{
name: InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
value: false.toString(),
},
{
name: InfraConfigEnum.ANALYTICS_USER_ID,
value: generateAnalyticsUserId(),
},
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: (await prisma.infraConfig.count()) === 0 ? 'true' : 'false',
},
];
return infraConfigDefaultObjs;
}
/**
* Verify if 'infra_config' table is loaded with all entries
* @returns boolean
*/
export async function isInfraConfigTablePopulated(): Promise<boolean> {
const prisma = new PrismaService();
try {
const dbInfraConfigs = await prisma.infraConfig.findMany();
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
const propsRemainingToInsert = infraConfigDefaultObjs.filter(
(p) => !dbInfraConfigs.find((e) => e.name === p.name),
);
if (propsRemainingToInsert.length > 0) {
console.log(
'Infra Config table is not populated with all entries. Populating now...',
);
return false;
}
return true;
} catch (error) {
return false;
}
}
/** /**
* Stop the app after 5 seconds * Stop the app after 5 seconds
* (Docker will re-start the app) * (Docker will re-start the app)
@@ -104,3 +234,12 @@ export function getConfiguredSSOProviders() {
return configuredAuthProviders.join(','); return configuredAuthProviders.join(',');
} }
/**
* Generate a hashed valued for analytics
* @returns Generated hashed value
*/
export function generateAnalyticsUserId() {
const hashedUserID = randomBytes(20).toString('hex');
return hashedUserID;
}

View File

@@ -0,0 +1,47 @@
import { Controller, Get, HttpStatus, Put, UseGuards } from '@nestjs/common';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { InfraConfigService } from './infra-config.service';
import * as E from 'fp-ts/Either';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { RESTAdminGuard } from 'src/admin/guards/rest-admin.guard';
import { RESTError } from 'src/types/RESTError';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'site', version: '1' })
export class SiteController {
constructor(private infraConfigService: InfraConfigService) {}
@Get('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async fetchSetupInfo() {
const status = await this.infraConfigService.get(
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
);
if (E.isLeft(status))
throwHTTPErr(<RESTError>{
message: status.left,
statusCode: HttpStatus.NOT_FOUND,
});
return status.right;
}
@Put('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async setSetupAsComplete() {
const res = await this.infraConfigService.update(
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
false.toString(),
false,
);
if (E.isLeft(res))
throwHTTPErr(<RESTError>{
message: res.left,
statusCode: HttpStatus.FORBIDDEN,
});
return res.right;
}
}

View File

@@ -1,6 +1,6 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig'; import { InfraConfigEnum } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper'; import { ServiceStatus } from './helper';
@ObjectType() @ObjectType()
@@ -8,7 +8,7 @@ export class InfraConfig {
@Field({ @Field({
description: 'Infra Config Name', description: 'Infra Config Name',
}) })
name: InfraConfigEnumForClient; name: InfraConfigEnum;
@Field({ @Field({
description: 'Infra Config Value', description: 'Infra Config Value',
@@ -16,7 +16,7 @@ export class InfraConfig {
value: string; value: string;
} }
registerEnumType(InfraConfigEnumForClient, { registerEnumType(InfraConfigEnum, {
name: 'InfraConfigEnum', name: 'InfraConfigEnum',
}); });

View File

@@ -1,10 +1,12 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { InfraConfigService } from './infra-config.service'; import { InfraConfigService } from './infra-config.service';
import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaModule } from 'src/prisma/prisma.module';
import { SiteController } from './infra-config.controller';
@Module({ @Module({
imports: [PrismaModule], imports: [PrismaModule],
providers: [InfraConfigService], providers: [InfraConfigService],
exports: [InfraConfigService], exports: [InfraConfigService],
controllers: [SiteController],
}) })
export class InfraConfigModule {} export class InfraConfigModule {}

View File

@@ -1,13 +1,16 @@
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigService } from './infra-config.service'; import { InfraConfigService } from './infra-config.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { import {
InfraConfigEnum, INFRA_CONFIG_NOT_FOUND,
InfraConfigEnumForClient, INFRA_CONFIG_OPERATION_NOT_ALLOWED,
} from 'src/types/InfraConfig'; INFRA_CONFIG_UPDATE_FAILED,
import { INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_UPDATE_FAILED } from 'src/errors'; } from 'src/errors';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import * as helper from './helper'; import * as helper from './helper';
import { InfraConfig as dbInfraConfig } from '@prisma/client';
import { InfraConfig } from './infra-config.model';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockConfigService = mockDeep<ConfigService>(); const mockConfigService = mockDeep<ConfigService>();
@@ -19,12 +22,82 @@ const infraConfigService = new InfraConfigService(
mockConfigService, mockConfigService,
); );
const INITIALIZED_DATE_CONST = new Date();
const dbInfraConfigs: dbInfraConfig[] = [
{
id: '3',
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: 'abcdefghijkl',
active: true,
createdOn: INITIALIZED_DATE_CONST,
updatedOn: INITIALIZED_DATE_CONST,
},
{
id: '4',
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: 'google',
active: true,
createdOn: INITIALIZED_DATE_CONST,
updatedOn: INITIALIZED_DATE_CONST,
},
];
const infraConfigs: InfraConfig[] = [
{
name: dbInfraConfigs[0].name as InfraConfigEnum,
value: dbInfraConfigs[0].value,
},
{
name: dbInfraConfigs[1].name as InfraConfigEnum,
value: dbInfraConfigs[1].value,
},
];
beforeEach(() => { beforeEach(() => {
mockReset(mockPrisma); mockReset(mockPrisma);
}); });
describe('InfraConfigService', () => { describe('InfraConfigService', () => {
describe('update', () => { describe('update', () => {
it('should update the infra config without backend server restart', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
const result = await infraConfigService.update(name, value);
expect(helper.stopApp).not.toHaveBeenCalled();
expect(result).toEqualRight({ name, value });
});
it('should update the infra config with backend server restart', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
const result = await infraConfigService.update(name, value, true);
expect(helper.stopApp).toHaveBeenCalledTimes(1);
expect(result).toEqualRight({ name, value });
});
it('should update the infra config', async () => { it('should update the infra config', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID; const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true'; const value = 'true';
@@ -71,7 +144,7 @@ describe('InfraConfigService', () => {
describe('get', () => { describe('get', () => {
it('should get the infra config', async () => { it('should get the infra config', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID; const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true'; const value = 'true';
mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({ mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({
@@ -87,7 +160,7 @@ describe('InfraConfigService', () => {
}); });
it('should pass correct params to prisma findUnique', async () => { it('should pass correct params to prisma findUnique', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID; const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
await infraConfigService.get(name); await infraConfigService.get(name);
@@ -98,7 +171,7 @@ describe('InfraConfigService', () => {
}); });
it('should throw an error if the infra config does not exist', async () => { it('should throw an error if the infra config does not exist', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID; const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
mockPrisma.infraConfig.findUniqueOrThrow.mockRejectedValueOnce('null'); mockPrisma.infraConfig.findUniqueOrThrow.mockRejectedValueOnce('null');
@@ -106,4 +179,45 @@ describe('InfraConfigService', () => {
expect(result).toEqualLeft(INFRA_CONFIG_NOT_FOUND); expect(result).toEqualLeft(INFRA_CONFIG_NOT_FOUND);
}); });
}); });
describe('getMany', () => {
it('should throw error if any disallowed names are provided', async () => {
const disallowedNames = [InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS];
const result = await infraConfigService.getMany(disallowedNames);
expect(result).toEqualLeft(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
});
it('should resolve right with disallowed names if `checkDisallowed` parameter passed', async () => {
const disallowedNames = [InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS];
const dbInfraConfigResponses = dbInfraConfigs.filter((dbConfig) =>
disallowedNames.includes(dbConfig.name as InfraConfigEnum),
);
mockPrisma.infraConfig.findMany.mockResolvedValueOnce(
dbInfraConfigResponses,
);
const result = await infraConfigService.getMany(disallowedNames, false);
expect(result).toEqualRight(
infraConfigs.filter((i) => disallowedNames.includes(i.name)),
);
});
it('should return right with infraConfigs if Prisma query succeeds', async () => {
const allowedNames = [InfraConfigEnum.GOOGLE_CLIENT_ID];
const dbInfraConfigResponses = dbInfraConfigs.filter((dbConfig) =>
allowedNames.includes(dbConfig.name as InfraConfigEnum),
);
mockPrisma.infraConfig.findMany.mockResolvedValueOnce(
dbInfraConfigResponses,
);
const result = await infraConfigService.getMany(allowedNames);
expect(result).toEqualRight(
infraConfigs.filter((i) => allowedNames.includes(i.name)),
);
});
});
}); });

View File

@@ -3,23 +3,25 @@ import { InfraConfig } from './infra-config.model';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfig as DBInfraConfig } from '@prisma/client'; import { InfraConfig as DBInfraConfig } from '@prisma/client';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { import { InfraConfigEnum } from 'src/types/InfraConfig';
InfraConfigEnum,
InfraConfigEnumForClient,
} from 'src/types/InfraConfig';
import { import {
AUTH_PROVIDER_NOT_SPECIFIED, AUTH_PROVIDER_NOT_SPECIFIED,
DATABASE_TABLE_NOT_EXIST, DATABASE_TABLE_NOT_EXIST,
INFRA_CONFIG_INVALID_INPUT, INFRA_CONFIG_INVALID_INPUT,
INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_NOT_FOUND,
INFRA_CONFIG_NOT_LISTED,
INFRA_CONFIG_RESET_FAILED, INFRA_CONFIG_RESET_FAILED,
INFRA_CONFIG_UPDATE_FAILED, INFRA_CONFIG_UPDATE_FAILED,
INFRA_CONFIG_SERVICE_NOT_CONFIGURED, INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
INFRA_CONFIG_OPERATION_NOT_ALLOWED,
} from 'src/errors'; } from 'src/errors';
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils'; import {
throwErr,
validateSMTPEmail,
validateSMTPUrl,
validateUrl,
} from 'src/utils';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper'; import { ServiceStatus, getDefaultInfraConfigs, stopApp } from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args'; import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
@@ -30,52 +32,22 @@ export class InfraConfigService implements OnModuleInit {
private readonly configService: ConfigService, private readonly configService: ConfigService,
) {} ) {}
async onModuleInit() { // Following fields are not updatable by `infraConfigs` Mutation. Use dedicated mutations for these fields instead.
await this.initializeInfraConfigTable(); EXCLUDE_FROM_UPDATE_CONFIGS = [
} InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
getDefaultInfraConfigs(): { name: InfraConfigEnum; value: string }[] { InfraConfigEnum.ANALYTICS_USER_ID,
// Prepare rows for 'infra_config' table with default values (from .env) for each 'name' InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [ ];
{ // Following fields can not be fetched by `infraConfigs` Query. Use dedicated queries for these fields instead.
name: InfraConfigEnum.MAILER_SMTP_URL, EXCLUDE_FROM_FETCH_CONFIGS = [
value: process.env.MAILER_SMTP_URL, InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
}, InfraConfigEnum.ANALYTICS_USER_ID,
{ InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
name: InfraConfigEnum.MAILER_ADDRESS_FROM,
value: process.env.MAILER_ADDRESS_FROM,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: process.env.GOOGLE_CLIENT_ID,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_SECRET,
value: process.env.GOOGLE_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_ID,
value: process.env.GITHUB_CLIENT_ID,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_SECRET,
value: process.env.GITHUB_CLIENT_SECRET,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_ID,
value: process.env.MICROSOFT_CLIENT_ID,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
value: process.env.MICROSOFT_CLIENT_SECRET,
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: getConfiguredSSOProviders(),
},
]; ];
return infraConfigDefaultObjs; async onModuleInit() {
await this.initializeInfraConfigTable();
} }
/** /**
@@ -84,16 +56,8 @@ export class InfraConfigService implements OnModuleInit {
*/ */
async initializeInfraConfigTable() { async initializeInfraConfigTable() {
try { try {
// Get all the 'names' of the properties to be saved in the 'infra_config' table
const enumValues = Object.values(InfraConfigEnum);
// Fetch the default values (value in .env) for configs to be saved in 'infra_config' table // Fetch the default values (value in .env) for configs to be saved in 'infra_config' table
const infraConfigDefaultObjs = this.getDefaultInfraConfigs(); const infraConfigDefaultObjs = await getDefaultInfraConfigs();
// Check if all the 'names' are listed in the default values
if (enumValues.length !== infraConfigDefaultObjs.length) {
throw new Error(INFRA_CONFIG_NOT_LISTED);
}
// Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table // Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table
const dbInfraConfigs = await this.prisma.infraConfig.findMany(); const dbInfraConfigs = await this.prisma.infraConfig.findMany();
@@ -147,12 +111,10 @@ export class InfraConfigService implements OnModuleInit {
* Update InfraConfig by name * Update InfraConfig by name
* @param name Name of the InfraConfig * @param name Name of the InfraConfig
* @param value Value of the InfraConfig * @param value Value of the InfraConfig
* @param restartEnabled If true, restart the app after updating the InfraConfig
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async update( async update(name: InfraConfigEnum, value: string, restartEnabled = false) {
name: InfraConfigEnumForClient | InfraConfigEnum,
value: string,
) {
const isValidate = this.validateEnvValues([{ name, value }]); const isValidate = this.validateEnvValues([{ name, value }]);
if (E.isLeft(isValidate)) return E.left(isValidate.left); if (E.isLeft(isValidate)) return E.left(isValidate.left);
@@ -162,7 +124,7 @@ export class InfraConfigService implements OnModuleInit {
data: { value }, data: { value },
}); });
stopApp(); if (restartEnabled) stopApp();
return E.right(this.cast(infraConfig)); return E.right(this.cast(infraConfig));
} catch (e) { } catch (e) {
@@ -176,6 +138,11 @@ export class InfraConfigService implements OnModuleInit {
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async updateMany(infraConfigs: InfraConfigArgs[]) { async updateMany(infraConfigs: InfraConfigArgs[]) {
for (let i = 0; i < infraConfigs.length; i++) {
if (this.EXCLUDE_FROM_UPDATE_CONFIGS.includes(infraConfigs[i].name))
return E.left(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
}
const isValidate = this.validateEnvValues(infraConfigs); const isValidate = this.validateEnvValues(infraConfigs);
if (E.isLeft(isValidate)) return E.left(isValidate.left); if (E.isLeft(isValidate)) return E.left(isValidate.left);
@@ -209,12 +176,26 @@ export class InfraConfigService implements OnModuleInit {
) { ) {
switch (service) { switch (service) {
case AuthProvider.GOOGLE: case AuthProvider.GOOGLE:
return configMap.GOOGLE_CLIENT_ID && configMap.GOOGLE_CLIENT_SECRET; return (
configMap.GOOGLE_CLIENT_ID &&
configMap.GOOGLE_CLIENT_SECRET &&
configMap.GOOGLE_CALLBACK_URL &&
configMap.GOOGLE_SCOPE
);
case AuthProvider.GITHUB: case AuthProvider.GITHUB:
return configMap.GITHUB_CLIENT_ID && configMap.GITHUB_CLIENT_SECRET; return (
configMap.GITHUB_CLIENT_ID &&
configMap.GITHUB_CLIENT_SECRET &&
configMap.GITHUB_CALLBACK_URL &&
configMap.GITHUB_SCOPE
);
case AuthProvider.MICROSOFT: case AuthProvider.MICROSOFT:
return ( return (
configMap.MICROSOFT_CLIENT_ID && configMap.MICROSOFT_CLIENT_SECRET configMap.MICROSOFT_CLIENT_ID &&
configMap.MICROSOFT_CLIENT_SECRET &&
configMap.MICROSOFT_CALLBACK_URL &&
configMap.MICROSOFT_SCOPE &&
configMap.MICROSOFT_TENANT
); );
case AuthProvider.EMAIL: case AuthProvider.EMAIL:
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM; return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
@@ -223,6 +204,22 @@ export class InfraConfigService implements OnModuleInit {
} }
} }
/**
* Enable or Disable Analytics Collection
*
* @param status Status to enable or disable
* @returns Boolean of status of analytics collection
*/
async toggleAnalyticsCollection(status: ServiceStatus) {
const isUpdated = await this.update(
InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
status === ServiceStatus.ENABLE ? 'true' : 'false',
);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(isUpdated.right.value === 'true');
}
/** /**
* Enable or Disable SSO for login/signup * Enable or Disable SSO for login/signup
* @param provider Auth Provider to enable or disable * @param provider Auth Provider to enable or disable
@@ -261,6 +258,7 @@ export class InfraConfigService implements OnModuleInit {
const isUpdated = await this.update( const isUpdated = await this.update(
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS, InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
updatedAuthProviders.join(','), updatedAuthProviders.join(','),
true,
); );
if (E.isLeft(isUpdated)) return E.left(isUpdated.left); if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
@@ -272,7 +270,7 @@ export class InfraConfigService implements OnModuleInit {
* @param name Name of the InfraConfig * @param name Name of the InfraConfig
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async get(name: InfraConfigEnumForClient) { async get(name: InfraConfigEnum) {
try { try {
const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({ const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({
where: { name }, where: { name },
@@ -289,7 +287,15 @@ export class InfraConfigService implements OnModuleInit {
* @param names Names of the InfraConfigs * @param names Names of the InfraConfigs
* @returns InfraConfig model * @returns InfraConfig model
*/ */
async getMany(names: InfraConfigEnumForClient[]) { async getMany(names: InfraConfigEnum[], checkDisallowedKeys: boolean = true) {
if (checkDisallowedKeys) {
// Check if the names are allowed to fetch by client
for (let i = 0; i < names.length; i++) {
if (this.EXCLUDE_FROM_FETCH_CONFIGS.includes(names[i]))
return E.left(INFRA_CONFIG_OPERATION_NOT_ALLOWED);
}
}
try { try {
const infraConfigs = await this.prisma.infraConfig.findMany({ const infraConfigs = await this.prisma.infraConfig.findMany({
where: { name: { in: names } }, where: { name: { in: names } },
@@ -316,13 +322,24 @@ export class InfraConfigService implements OnModuleInit {
*/ */
async reset() { async reset() {
try { try {
const infraConfigDefaultObjs = this.getDefaultInfraConfigs(); const infraConfigDefaultObjs = await getDefaultInfraConfigs();
await this.prisma.infraConfig.deleteMany({ await this.prisma.infraConfig.deleteMany({
where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } }, where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } },
}); });
// Hardcode t
const updatedInfraConfigDefaultObjs = infraConfigDefaultObjs.filter(
(obj) => obj.name !== InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
);
await this.prisma.infraConfig.createMany({ await this.prisma.infraConfig.createMany({
data: infraConfigDefaultObjs, data: [
...updatedInfraConfigDefaultObjs,
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: 'true',
},
],
}); });
stopApp(); stopApp();
@@ -338,36 +355,60 @@ export class InfraConfigService implements OnModuleInit {
*/ */
validateEnvValues( validateEnvValues(
infraConfigs: { infraConfigs: {
name: InfraConfigEnumForClient | InfraConfigEnum; name: InfraConfigEnum;
value: string; value: string;
}[], }[],
) { ) {
for (let i = 0; i < infraConfigs.length; i++) { for (let i = 0; i < infraConfigs.length; i++) {
switch (infraConfigs[i].name) { switch (infraConfigs[i].name) {
case InfraConfigEnumForClient.MAILER_SMTP_URL: case InfraConfigEnum.MAILER_SMTP_URL:
const isValidUrl = validateSMTPUrl(infraConfigs[i].value); const isValidUrl = validateSMTPUrl(infraConfigs[i].value);
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM: case InfraConfigEnum.MAILER_ADDRESS_FROM:
const isValidEmail = validateSMTPEmail(infraConfigs[i].value); const isValidEmail = validateSMTPEmail(infraConfigs[i].value);
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID: case InfraConfigEnum.GOOGLE_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_SECRET: case InfraConfigEnum.GOOGLE_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.GITHUB_CLIENT_ID: case InfraConfigEnum.GOOGLE_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GOOGLE_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.GITHUB_CLIENT_SECRET: case InfraConfigEnum.GITHUB_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_ID: case InfraConfigEnum.GITHUB_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_SECRET: case InfraConfigEnum.GITHUB_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.GITHUB_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_CALLBACK_URL:
if (!validateUrl(infraConfigs[i].value))
return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_SCOPE:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnum.MICROSOFT_TENANT:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT); if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break; break;
default: default:

View File

@@ -1,14 +1,14 @@
import { Field, InputType } from '@nestjs/graphql'; import { Field, InputType } from '@nestjs/graphql';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig'; import { InfraConfigEnum } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper'; import { ServiceStatus } from './helper';
import { AuthProvider } from 'src/auth/helper'; import { AuthProvider } from 'src/auth/helper';
@InputType() @InputType()
export class InfraConfigArgs { export class InfraConfigArgs {
@Field(() => InfraConfigEnumForClient, { @Field(() => InfraConfigEnum, {
description: 'Infra Config Name', description: 'Infra Config Name',
}) })
name: InfraConfigEnumForClient; name: InfraConfigEnum;
@Field({ @Field({
description: 'Infra Config Value', description: 'Infra Config Value',

View File

@@ -25,7 +25,7 @@ export class MailerService {
): string { ): string {
switch (mailDesc.template) { switch (mailDesc.template) {
case 'team-invitation': case 'team-invitation':
return `${mailDesc.variables.invitee} invited you to join ${mailDesc.variables.invite_team_name} in Hoppscotch`; return `A user has invited you to join a team workspace in Hoppscotch`;
case 'user-invitation': case 'user-invitation':
return 'Sign in to Hoppscotch'; return 'Sign in to Hoppscotch';

View File

@@ -27,6 +27,12 @@
color: #3869D4; color: #3869D4;
} }
a.nohighlight {
color: inherit !important;
text-decoration: none !important;
cursor: default !important;
}
a img { a img {
border: none; border: none;
} }
@@ -458,7 +464,7 @@
<td class="content-cell"> <td class="content-cell">
<div class="f-fallback"> <div class="f-fallback">
<h1>Hi there,</h1> <h1>Hi there,</h1>
<p>{{invitee}} with {{invite_team_name}} has invited you to use Hoppscotch to collaborate with them. Click the button below to set up your account and get started:</p> <p><a class="nohighlight" name="invitee" href="#">{{invitee}}</a> with <a class="nohighlight" name="invite_team_name" href="#">{{invite_team_name}}</a> has invited you to use Hoppscotch to collaborate with them. Click the button below to set up your account and get started:</p>
<!-- Action --> <!-- Action -->
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0"> <table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr> <tr>
@@ -484,7 +490,7 @@
Welcome aboard, <br /> Welcome aboard, <br />
Your friends at Hoppscotch Your friends at Hoppscotch
</p> </p>
<p><strong>P.S.</strong> If you don't associate with {{invitee}} or {{invite_team_name}}, just ignore this email.</p> <p><strong>P.S.</strong> If you don't associate with <a class="nohighlight" name="invitee" href="#">{{invitee}}</a> or <a class="nohighlight" name="invite_team_name" href="#">{{invite_team_name}}</a>, just ignore this email.</p>
<!-- Sub copy --> <!-- Sub copy -->
<table class="body-sub"> <table class="body-sub">
<tr> <tr>

View File

@@ -27,6 +27,12 @@
color: #3869D4; color: #3869D4;
} }
a.nohighlight {
color: inherit !important;
text-decoration: none !important;
cursor: default !important;
}
a img { a img {
border: none; border: none;
} }

View File

@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { PosthogService } from './posthog.service';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
imports: [PrismaModule],
providers: [PosthogService],
})
export class PosthogModule {}

View File

@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common';
import { PostHog } from 'posthog-node';
import { Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from 'src/prisma/prisma.service';
import { CronJob } from 'cron';
import { POSTHOG_CLIENT_NOT_INITIALIZED } from 'src/errors';
import { throwErr } from 'src/utils';
@Injectable()
export class PosthogService {
private postHogClient: PostHog;
private POSTHOG_API_KEY = 'phc_9CipPajQC22mSkk2wxe2TXsUA0Ysyupe8dt5KQQELqx';
constructor(
private readonly configService: ConfigService,
private readonly prismaService: PrismaService,
private schedulerRegistry: SchedulerRegistry,
) {}
async onModuleInit() {
if (this.configService.get('INFRA.ALLOW_ANALYTICS_COLLECTION') === 'true') {
console.log('Initializing PostHog');
this.postHogClient = new PostHog(this.POSTHOG_API_KEY, {
host: 'https://eu.posthog.com',
});
// Schedule the cron job only if analytics collection is allowed
this.scheduleCronJob();
}
}
private scheduleCronJob() {
const job = new CronJob(CronExpression.EVERY_WEEK, async () => {
await this.capture();
});
this.schedulerRegistry.addCronJob('captureAnalytics', job);
job.start();
}
async capture() {
if (!this.postHogClient) {
throwErr(POSTHOG_CLIENT_NOT_INITIALIZED);
}
this.postHogClient.capture({
distinctId: this.configService.get('INFRA.ANALYTICS_USER_ID'),
event: 'sh_instance',
properties: {
type: 'COMMUNITY',
total_user_count: await this.prismaService.user.count(),
total_workspace_count: await this.prismaService.team.count(),
version: this.configService.get('npm_package_version'),
},
});
console.log('Sent event to PostHog');
}
}

View File

@@ -0,0 +1,14 @@
// Type of data returned from the query to obtain all search results
export type SearchQueryReturnType = {
id: string;
title: string;
type: 'collection' | 'request';
method?: string;
};
// Type of data returned from the query to obtain all parents
export type ParentTreeQueryReturnType = {
id: string;
parentID: string;
title: string;
};

View File

@@ -0,0 +1,38 @@
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
import { TeamCollectionService } from './team-collection.service';
import * as E from 'fp-ts/Either';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { RequiresTeamRole } from 'src/team/decorators/requires-team-role.decorator';
import { TeamMemberRole } from '@prisma/client';
import { RESTTeamMemberGuard } from 'src/team/guards/rest-team-member.guard';
import { throwHTTPErr } from 'src/utils';
@UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'team-collection', version: '1' })
export class TeamCollectionController {
constructor(private readonly teamCollectionService: TeamCollectionService) {}
@Get('search/:teamID/:searchQuery')
@RequiresTeamRole(
TeamMemberRole.VIEWER,
TeamMemberRole.EDITOR,
TeamMemberRole.OWNER,
)
@UseGuards(JwtAuthGuard, RESTTeamMemberGuard)
async searchByTitle(
@Param('searchQuery') searchQuery: string,
@Param('teamID') teamID: string,
@Query('take') take: string,
@Query('skip') skip: string,
) {
const res = await this.teamCollectionService.searchByTitle(
searchQuery,
teamID,
parseInt(take),
parseInt(skip),
);
if (E.isLeft(res)) throwHTTPErr(res.left);
return res.right;
}
}

View File

@@ -6,6 +6,7 @@ import { GqlCollectionTeamMemberGuard } from './guards/gql-collection-team-membe
import { TeamModule } from '../team/team.module'; import { TeamModule } from '../team/team.module';
import { UserModule } from '../user/user.module'; import { UserModule } from '../user/user.module';
import { PubSubModule } from '../pubsub/pubsub.module'; import { PubSubModule } from '../pubsub/pubsub.module';
import { TeamCollectionController } from './team-collection.controller';
@Module({ @Module({
imports: [PrismaModule, TeamModule, UserModule, PubSubModule], imports: [PrismaModule, TeamModule, UserModule, PubSubModule],
@@ -15,5 +16,6 @@ import { PubSubModule } from '../pubsub/pubsub.module';
GqlCollectionTeamMemberGuard, GqlCollectionTeamMemberGuard,
], ],
exports: [TeamCollectionService, GqlCollectionTeamMemberGuard], exports: [TeamCollectionService, GqlCollectionTeamMemberGuard],
controllers: [TeamCollectionController],
}) })
export class TeamCollectionModule {} export class TeamCollectionModule {}

View File

@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common'; import { HttpStatus, Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
import { TeamCollection } from './team-collection.model'; import { TeamCollection } from './team-collection.model';
import { import {
@@ -14,6 +14,10 @@ import {
TEAM_COL_SAME_NEXT_COLL, TEAM_COL_SAME_NEXT_COLL,
TEAM_COL_REORDERING_FAILED, TEAM_COL_REORDERING_FAILED,
TEAM_COLL_DATA_INVALID, TEAM_COLL_DATA_INVALID,
TEAM_REQ_SEARCH_FAILED,
TEAM_COL_SEARCH_FAILED,
TEAM_REQ_PARENT_TREE_GEN_FAILED,
TEAM_COLL_PARENT_TREE_GEN_FAILED,
} from '../errors'; } from '../errors';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { isValidLength } from 'src/utils'; import { isValidLength } from 'src/utils';
@@ -22,6 +26,9 @@ import * as O from 'fp-ts/Option';
import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client'; import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client';
import { CollectionFolder } from 'src/types/CollectionFolder'; import { CollectionFolder } from 'src/types/CollectionFolder';
import { stringToJson } from 'src/utils'; import { stringToJson } from 'src/utils';
import { CollectionSearchNode } from 'src/types/CollectionSearchNode';
import { ParentTreeQueryReturnType, SearchQueryReturnType } from './helper';
import { RESTError } from 'src/types/RESTError';
@Injectable() @Injectable()
export class TeamCollectionService { export class TeamCollectionService {
@@ -1056,4 +1063,266 @@ export class TeamCollectionService {
return E.left(TEAM_COLL_NOT_FOUND); return E.left(TEAM_COLL_NOT_FOUND);
} }
} }
/**
* Search for TeamCollections and TeamRequests by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
async searchByTitle(
searchQuery: string,
teamID: string,
take = 10,
skip = 0,
) {
// Fetch all collections and requests that match the search query
const searchResults: SearchQueryReturnType[] = [];
const matchedCollections = await this.searchCollections(
searchQuery,
teamID,
take,
skip,
);
if (E.isLeft(matchedCollections))
return E.left(<RESTError>{
message: matchedCollections.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResults.push(...matchedCollections.right);
const matchedRequests = await this.searchRequests(
searchQuery,
teamID,
take,
skip,
);
if (E.isLeft(matchedRequests))
return E.left(<RESTError>{
message: matchedRequests.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResults.push(...matchedRequests.right);
// Generate the parent tree for searchResults
const searchResultsWithTree: CollectionSearchNode[] = [];
for (let i = 0; i < searchResults.length; i++) {
const fetchedParentTree = await this.fetchParentTree(searchResults[i]);
if (E.isLeft(fetchedParentTree))
return E.left(<RESTError>{
message: fetchedParentTree.left,
statusCode: HttpStatus.NOT_FOUND,
});
searchResultsWithTree.push({
type: searchResults[i].type,
title: searchResults[i].title,
method: searchResults[i].method,
id: searchResults[i].id,
path: !fetchedParentTree
? []
: ([fetchedParentTree.right] as CollectionSearchNode[]),
});
}
return E.right({ data: searchResultsWithTree });
}
/**
* Search for TeamCollections by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
private async searchCollections(
searchQuery: string,
teamID: string,
take: number,
skip: number,
) {
const query = Prisma.sql`
select id,title,'collection' AS type
from "TeamCollection"
where "TeamCollection"."teamID"=${teamID}
and titlesearch @@ to_tsquery(${searchQuery})
order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
limit ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`;
try {
const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query);
return E.right(res);
} catch (error) {
return E.left(TEAM_COL_SEARCH_FAILED);
}
}
/**
* Search for TeamRequests by title
*
* @param searchQuery The search query
* @param teamID The Team ID
* @param take Number of items we want returned
* @param skip Number of items we want to skip
* @returns An Either of the search results
*/
private async searchRequests(
searchQuery: string,
teamID: string,
take: number,
skip: number,
) {
const query = Prisma.sql`
select id,title,request->>'method' as method,'request' AS type
from "TeamRequest"
where "TeamRequest"."teamID"=${teamID}
and titlesearch @@ to_tsquery(${searchQuery})
order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
limit ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`;
try {
const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query);
return E.right(res);
} catch (error) {
return E.left(TEAM_REQ_SEARCH_FAILED);
}
}
/**
* Generate the parent tree of a search result
*
* @param searchResult The search result for which we want to generate the parent tree
* @returns The parent tree of the search result
*/
private async fetchParentTree(searchResult: SearchQueryReturnType) {
return searchResult.type === 'collection'
? await this.fetchCollectionParentTree(searchResult.id)
: await this.fetchRequestParentTree(searchResult.id);
}
/**
* Generate the parent tree of a collection
*
* @param id The ID of the collection
* @returns The parent tree of the collection
*/
private async fetchCollectionParentTree(id: string) {
try {
const query = Prisma.sql`
WITH RECURSIVE collection_tree AS (
SELECT tc.id, tc."parentID", tc.title
FROM "TeamCollection" AS tc
JOIN "TeamCollection" AS tr ON tc.id = tr."parentID"
WHERE tr.id = ${id}
UNION ALL
SELECT parent.id, parent."parentID", parent.title
FROM "TeamCollection" AS parent
JOIN collection_tree AS ct ON parent.id = ct."parentID"
)
SELECT * FROM collection_tree;
`;
const res = await this.prisma.$queryRaw<ParentTreeQueryReturnType[]>(
query,
);
const collectionParentTree = this.generateParentTree(res);
return E.right(collectionParentTree);
} catch (error) {
E.left(TEAM_COLL_PARENT_TREE_GEN_FAILED);
}
}
/**
* Generate the parent tree from the collections
*
* @param parentCollections The parent collections
* @returns The parent tree of the parent collections
*/
private generateParentTree(parentCollections: ParentTreeQueryReturnType[]) {
function findChildren(id) {
const collection = parentCollections.filter((item) => item.id === id)[0];
if (collection.parentID == null) {
return {
id: collection.id,
title: collection.title,
type: 'collection',
path: [],
};
}
const res = {
id: collection.id,
title: collection.title,
type: 'collection',
path: findChildren(collection.parentID),
};
return res;
}
if (parentCollections.length > 0) {
if (parentCollections[0].parentID == null) {
return {
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: [],
};
}
return {
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: findChildren(parentCollections[0].parentID),
};
}
return null;
}
/**
* Generate the parent tree of a request
*
* @param id The ID of the request
* @returns The parent tree of the request
*/
private async fetchRequestParentTree(id: string) {
try {
const query = Prisma.sql`
WITH RECURSIVE request_collection_tree AS (
SELECT tc.id, tc."parentID", tc.title
FROM "TeamCollection" AS tc
JOIN "TeamRequest" AS tr ON tc.id = tr."collectionID"
WHERE tr.id = ${id}
UNION ALL
SELECT parent.id, parent."parentID", parent.title
FROM "TeamCollection" AS parent
JOIN request_collection_tree AS ct ON parent.id = ct."parentID"
)
SELECT * FROM request_collection_tree;
`;
const res = await this.prisma.$queryRaw<ParentTreeQueryReturnType[]>(
query,
);
const requestParentTree = this.generateParentTree(res);
return E.right(requestParentTree);
} catch (error) {
return E.left(TEAM_REQ_PARENT_TREE_GEN_FAILED);
}
}
} }

View File

@@ -0,0 +1,47 @@
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { TeamService } from '../../team/team.service';
import { TeamMemberRole } from '../../team/team.model';
import {
BUG_TEAM_NO_REQUIRE_TEAM_ROLE,
BUG_AUTH_NO_USER_CTX,
BUG_TEAM_NO_TEAM_ID,
TEAM_MEMBER_NOT_FOUND,
TEAM_NOT_REQUIRED_ROLE,
} from 'src/errors';
import { throwHTTPErr } from 'src/utils';
@Injectable()
export class RESTTeamMemberGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private readonly teamService: TeamService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requireRoles = this.reflector.get<TeamMemberRole[]>(
'requiresTeamRole',
context.getHandler(),
);
if (!requireRoles)
throwHTTPErr({ message: BUG_TEAM_NO_REQUIRE_TEAM_ROLE, statusCode: 400 });
const request = context.switchToHttp().getRequest();
const { user } = request;
if (user == undefined)
throwHTTPErr({ message: BUG_AUTH_NO_USER_CTX, statusCode: 400 });
const teamID = request.params.teamID;
if (!teamID)
throwHTTPErr({ message: BUG_TEAM_NO_TEAM_ID, statusCode: 400 });
const teamMember = await this.teamService.getTeamMember(teamID, user.uid);
if (!teamMember)
throwHTTPErr({ message: TEAM_MEMBER_NOT_FOUND, statusCode: 404 });
if (requireRoles.includes(teamMember.role)) return true;
throwHTTPErr({ message: TEAM_NOT_REQUIRED_ROLE, statusCode: 403 });
}
}

View File

@@ -0,0 +1,17 @@
// Response type of results from the search query
export type CollectionSearchNode = {
/** Encodes the hierarchy of where the node is **/
path: CollectionSearchNode[];
} & (
| {
type: 'request';
title: string;
method: string;
id: string;
}
| {
type: 'collection';
title: string;
id: string;
}
);

View File

@@ -4,26 +4,23 @@ export enum InfraConfigEnum {
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID', GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET', GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GOOGLE_CALLBACK_URL = 'GOOGLE_CALLBACK_URL',
GOOGLE_SCOPE = 'GOOGLE_SCOPE',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID', GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET', GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
GITHUB_CALLBACK_URL = 'GITHUB_CALLBACK_URL',
GITHUB_SCOPE = 'GITHUB_SCOPE',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID', MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET', MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
MICROSOFT_CALLBACK_URL = 'MICROSOFT_CALLBACK_URL',
MICROSOFT_SCOPE = 'MICROSOFT_SCOPE',
MICROSOFT_TENANT = 'MICROSOFT_TENANT',
VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS', VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS',
}
ALLOW_ANALYTICS_COLLECTION = 'ALLOW_ANALYTICS_COLLECTION',
export enum InfraConfigEnumForClient { ANALYTICS_USER_ID = 'ANALYTICS_USER_ID',
MAILER_SMTP_URL = 'MAILER_SMTP_URL', IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP',
MAILER_ADDRESS_FROM = 'MAILER_ADDRESS_FROM',
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
} }

View File

@@ -1,10 +1,10 @@
import { HttpStatus } from '@nestjs/common'; import { HttpStatus } from '@nestjs/common';
/** /**
** Custom interface to handle errors specific to Auth module ** Custom interface to handle errors for REST modules such as Auth, Admin modules
** Since its REST we need to return the HTTP status code along with the error message ** Since its REST we need to return the HTTP status code along with the error message
*/ */
export type AuthError = { export type RESTError = {
message: string; message: string;
statusCode: HttpStatus; statusCode: HttpStatus;
}; };

View File

@@ -17,3 +17,21 @@ export class PaginationArgs {
}) })
take: number; take: number;
} }
@ArgsType()
@InputType()
export class OffsetPaginationArgs {
@Field({
nullable: true,
defaultValue: 0,
description: 'Number of items to skip',
})
skip: number;
@Field({
nullable: true,
defaultValue: 10,
description: 'Number of items to fetch',
})
take: number;
}

View File

@@ -56,3 +56,22 @@ export enum SessionType {
registerEnumType(SessionType, { registerEnumType(SessionType, {
name: 'SessionType', name: 'SessionType',
}); });
@ObjectType()
export class UserDeletionResult {
@Field(() => ID, {
description: 'UID of the user',
})
userUID: string;
@Field(() => Boolean, {
description: 'Flag to determine if user deletion was successful or not',
})
isDeleted: Boolean;
@Field({
nullable: true,
description: 'Error message if user deletion was not successful',
})
errorMessage: String;
}

View File

@@ -1,4 +1,4 @@
import { JSON_INVALID, USER_NOT_FOUND } from 'src/errors'; import { JSON_INVALID, USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors';
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
@@ -176,6 +176,26 @@ describe('UserService', () => {
}); });
}); });
describe('findUsersByIds', () => {
test('should successfully return users given valid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.findUsersByIds([
'123344',
'5555',
'6666',
]);
expect(result).toEqual(users);
});
test('should return empty array of users given a invalid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.findUsersByIds(['sdcvbdbr']);
expect(result).toEqual([]);
});
});
describe('createUserViaMagicLink', () => { describe('createUserViaMagicLink', () => {
test('should successfully create user and account for magic-link given valid inputs', async () => { test('should successfully create user and account for magic-link given valid inputs', async () => {
mockPrisma.user.create.mockResolvedValueOnce(user); mockPrisma.user.create.mockResolvedValueOnce(user);
@@ -414,6 +434,54 @@ describe('UserService', () => {
}); });
}); });
describe('updateUserDisplayName', () => {
test('should resolve right and update user display name', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
const result = await userService.updateUserDisplayName(
user.uid,
newDisplayName,
);
expect(result).toEqualRight({
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
});
});
test('should resolve right and publish user updated subscription', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
await userService.updateUserDisplayName(user.uid, user.displayName);
expect(mockPubSub.publish).toHaveBeenCalledWith(
`user/${user.uid}/updated`,
{
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
},
);
});
test('should resolve left and error when invalid user uid is passed', async () => {
mockPrisma.user.update.mockRejectedValueOnce('NotFoundError');
const result = await userService.updateUserDisplayName(
'invalidUserUid',
user.displayName,
);
expect(result).toEqualLeft(USER_NOT_FOUND);
});
});
describe('fetchAllUsers', () => { describe('fetchAllUsers', () => {
test('should resolve right and return 20 users when cursor is null', async () => { test('should resolve right and return 20 users when cursor is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users); mockPrisma.user.findMany.mockResolvedValueOnce(users);
@@ -435,6 +503,36 @@ describe('UserService', () => {
}); });
}); });
describe('fetchAllUsersV2', () => {
test('should resolve right and return first 20 users when searchString is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2(null, {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve right and return next 20 users when searchString is provided', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2('.com', {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve left and return an empty array when users not found', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.fetchAllUsersV2('Unknown entry', {
take: 20,
skip: 0,
});
expect(result).toEqual([]);
});
});
describe('fetchAdminUsers', () => { describe('fetchAdminUsers', () => {
test('should return a list of admin users', async () => { test('should return a list of admin users', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers); mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers);
@@ -556,4 +654,17 @@ describe('UserService', () => {
expect(result).toEqual(10); expect(result).toEqual(10);
}); });
}); });
describe('removeUsersAsAdmin', () => {
test('should resolve right and return true for valid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 1 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualRight(true);
});
test('should resolve right and return false for invalid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 0 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualLeft(USERS_NOT_FOUND);
});
});
}); });

View File

@@ -8,13 +8,14 @@ import * as T from 'fp-ts/Task';
import * as A from 'fp-ts/Array'; import * as A from 'fp-ts/Array';
import { pipe, constVoid } from 'fp-ts/function'; import { pipe, constVoid } from 'fp-ts/function';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { USER_NOT_FOUND } from 'src/errors'; import { USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors';
import { SessionType, User } from './user.model'; import { SessionType, User } from './user.model';
import { USER_UPDATE_FAILED } from 'src/errors'; import { USER_UPDATE_FAILED } from 'src/errors';
import { PubSubService } from 'src/pubsub/pubsub.service'; import { PubSubService } from 'src/pubsub/pubsub.service';
import { stringToJson, taskEitherValidateArraySeq } from 'src/utils'; import { stringToJson, taskEitherValidateArraySeq } from 'src/utils';
import { UserDataHandler } from './user.data.handler'; import { UserDataHandler } from './user.data.handler';
import { User as DbUser } from '@prisma/client'; import { User as DbUser } from '@prisma/client';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
@Injectable() @Injectable()
export class UserService { export class UserService {
@@ -88,6 +89,20 @@ export class UserService {
} }
} }
/**
* Find users with given IDs
* @param userUIDs User IDs
* @returns Array of found Users
*/
async findUsersByIds(userUIDs: string[]): Promise<AuthUser[]> {
const users = await this.prisma.user.findMany({
where: {
uid: { in: userUIDs },
},
});
return users;
}
/** /**
* Update User with new generated hashed refresh token * Update User with new generated hashed refresh token
* *
@@ -269,6 +284,30 @@ export class UserService {
} }
} }
/**
* Update a user's data
* @param userUID User UID
* @param displayName User's displayName
* @returns a Either of User or error
*/
async updateUserDisplayName(userUID: string, displayName: string) {
try {
const dbUpdatedUser = await this.prisma.user.update({
where: { uid: userUID },
data: { displayName },
});
const updatedUser = this.convertDbUserToUser(dbUpdatedUser);
// Publish subscription for user updates
await this.pubsub.publish(`user/${updatedUser.uid}/updated`, updatedUser);
return E.right(updatedUser);
} catch (error) {
return E.left(USER_NOT_FOUND);
}
}
/** /**
* Validate and parse currentRESTSession and currentGQLSession * Validate and parse currentRESTSession and currentGQLSession
* @param sessionData string of the session * @param sessionData string of the session
@@ -286,6 +325,7 @@ export class UserService {
* @param cursorID string of userUID or null * @param cursorID string of userUID or null
* @param take number of users to query * @param take number of users to query
* @returns an array of `User` object * @returns an array of `User` object
* @deprecated use fetchAllUsersV2 instead
*/ */
async fetchAllUsers(cursorID: string, take: number) { async fetchAllUsers(cursorID: string, take: number) {
const fetchedUsers = await this.prisma.user.findMany({ const fetchedUsers = await this.prisma.user.findMany({
@@ -296,6 +336,43 @@ export class UserService {
return fetchedUsers; return fetchedUsers;
} }
/**
* Fetch all the users in the `User` table based on cursor
* @param searchString search on user's displayName or email
* @param paginationOption pagination options
* @returns an array of `User` object
*/
async fetchAllUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const fetchedUsers = await this.prisma.user.findMany({
skip: paginationOption.skip,
take: paginationOption.take,
where: searchString
? {
OR: [
{
displayName: {
contains: searchString,
mode: 'insensitive',
},
},
{
email: {
contains: searchString,
mode: 'insensitive',
},
},
],
}
: undefined,
orderBy: [{ isAdmin: 'desc' }, { displayName: 'asc' }],
});
return fetchedUsers;
}
/** /**
* Fetch the number of users in db * Fetch the number of users in db
* @returns a count (Int) of user records in DB * @returns a count (Int) of user records in DB
@@ -326,6 +403,23 @@ export class UserService {
} }
} }
/**
* Change users to admins by toggling isAdmin param to true
* @param userUID user UIDs
* @returns a Either of true or error
*/
async makeAdmins(userUIDs: string[]) {
try {
await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: true },
});
return E.right(true);
} catch (error) {
return E.left(USER_UPDATE_FAILED);
}
}
/** /**
* Fetch all the admin users * Fetch all the admin users
* @returns an array of admin users * @returns an array of admin users
@@ -444,4 +538,22 @@ export class UserService {
return E.left(USER_NOT_FOUND); return E.left(USER_NOT_FOUND);
} }
} }
/**
* Change users from an admin by toggling isAdmin param to false
* @param userUIDs user UIDs
* @returns a Either of true or error
*/
async removeUsersAsAdmin(userUIDs: string[]) {
const data = await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: false },
});
if (data.count === 0) {
return E.left(USERS_NOT_FOUND);
}
return E.right(true);
}
} }

View File

@@ -1,4 +1,4 @@
import { ExecutionContext } from '@nestjs/common'; import { ExecutionContext, HttpException } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql'; import { GqlExecutionContext } from '@nestjs/graphql';
import { pipe } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/function';
@@ -16,6 +16,7 @@ import {
JSON_INVALID, JSON_INVALID,
} from './errors'; } from './errors';
import { AuthProvider } from './auth/helper'; import { AuthProvider } from './auth/helper';
import { RESTError } from './types/RESTError';
/** /**
* A workaround to throw an exception in an expression. * A workaround to throw an exception in an expression.
@@ -27,6 +28,15 @@ export function throwErr(errMessage: string): never {
throw new Error(errMessage); throw new Error(errMessage);
} }
/**
* This function allows throw to be used as an expression
* @param errMessage Message present in the error message
*/
export function throwHTTPErr(errorData: RESTError): never {
const { message, statusCode } = errorData;
throw new HttpException(message, statusCode);
}
/** /**
* Prints the given value to log and returns the same value. * Prints the given value to log and returns the same value.
* Used for debugging functional pipelines. * Used for debugging functional pipelines.
@@ -173,6 +183,16 @@ export const validateSMTPUrl = (url: string) => {
return false; return false;
}; };
/**
* Checks to see if the URL is valid or not
* @param url The URL to validate
* @returns boolean
*/
export const validateUrl = (url: string) => {
const urlRegex = /^(http|https):\/\/[^ "]+$/;
return urlRegex.test(url);
};
/** /**
* String to JSON parser * String to JSON parser
* @param {str} str The string to parse * @param {str} str The string to parse

View File

@@ -1,3 +0,0 @@
#!/usr/bin/env node
// * The entry point of the CLI
require("../dist").cli(process.argv);

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env node
// * The entry point of the CLI
import { cli } from "../dist/index.js";
cli(process.argv);

View File

@@ -1,11 +1,12 @@
{ {
"name": "@hoppscotch/cli", "name": "@hoppscotch/cli",
"version": "0.5.1", "version": "0.6.0",
"description": "A CLI to run Hoppscotch test scripts in CI environments.", "description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io", "homepage": "https://hoppscotch.io",
"type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
"hopp": "bin/hopp" "hopp": "bin/hopp.js"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -39,27 +40,31 @@
}, },
"license": "MIT", "license": "MIT",
"private": false, "private": false,
"dependencies": {
"axios": "^1.6.6",
"chalk": "^5.3.0",
"commander": "^11.1.0",
"lodash-es": "^4.17.21",
"qs": "^6.11.2",
"zod": "^3.22.4"
},
"devDependencies": { "devDependencies": {
"@hoppscotch/data": "workspace:^", "@hoppscotch/data": "workspace:^",
"@hoppscotch/js-sandbox": "workspace:^", "@hoppscotch/js-sandbox": "workspace:^",
"@relmify/jest-fp-ts": "^2.1.1", "@relmify/jest-fp-ts": "^2.1.1",
"@swc/core": "^1.3.92", "@swc/core": "^1.3.105",
"@types/jest": "^29.5.5", "@types/jest": "^29.5.11",
"@types/lodash": "^4.14.199", "@types/lodash-es": "^4.17.12",
"@types/qs": "^6.9.8", "@types/qs": "^6.9.11",
"axios": "^0.21.4", "fp-ts": "^2.16.2",
"chalk": "^4.1.2",
"commander": "^11.0.0",
"esm": "^3.2.25",
"fp-ts": "^2.16.1",
"io-ts": "^2.2.20",
"jest": "^29.7.0", "jest": "^29.7.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"prettier": "^3.0.3", "prettier": "^3.2.4",
"qs": "^6.11.2", "qs": "^6.11.2",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.2",
"tsup": "^7.2.0", "tsup": "^8.0.1",
"typescript": "^5.2.2", "typescript": "^5.3.3",
"verzod": "^0.2.2",
"zod": "^3.22.4" "zod": "^3.22.4"
} }
} }

View File

@@ -3,8 +3,9 @@ import { ExecException } from "child_process";
import { HoppErrorCode } from "../../types/errors"; import { HoppErrorCode } from "../../types/errors";
import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils"; import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils";
describe("Test 'hopp test <file>' command:", () => { describe("Test `hopp test <file>` command:", () => {
test("No collection file path provided.", async () => { describe("Argument parsing", () => {
test("Errors with the code `INVALID_ARGUMENT` for not supplying enough arguments", async () => {
const args = "test"; const args = "test";
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -12,7 +13,17 @@ describe("Test 'hopp test <file>' command:", () => {
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Collection file not found.", async () => { test("Errors with the code `INVALID_ARGUMENT` for an invalid command", async () => {
const args = "invalid-arg";
const { stderr } = await runCLI(args);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
});
})
describe("Supplied collection export file validations", () => {
test("Errors with the code `FILE_NOT_FOUND` if the supplied collection export file doesn't exist", async () => {
const args = "test notfound.json"; const args = "test notfound.json";
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -20,44 +31,32 @@ describe("Test 'hopp test <file>' command:", () => {
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND"); expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
}); });
test("Collection file is invalid JSON.", async () => { test("Errors with the code UNKNOWN_ERROR if the supplied collection export file content isn't valid JSON", async () => {
const args = `test ${getTestJsonFilePath( const args = `test ${getTestJsonFilePath("malformed-coll.json", "collection")}`;
"malformed-collection.json"
)}`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR"); expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR");
}); });
test("Malformed collection file.", async () => { test("Errors with the code `MALFORMED_COLLECTION` if the supplied collection export file content is malformed", async () => {
const args = `test ${getTestJsonFilePath( const args = `test ${getTestJsonFilePath("malformed-coll-2.json", "collection")}`;
"malformed-collection2.json"
)}`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION"); expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
}); });
test("Invalid arguement.", async () => { test("Errors with the code `INVALID_FILE_TYPE` if the supplied collection export file doesn't end with the `.json` extension", async () => {
const args = "invalid-arg"; const args = `test ${getTestJsonFilePath("notjson-coll.txt", "collection")}`;
const { stderr } = await runCLI(args);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
});
test("Collection file not JSON type.", async () => {
const args = `test ${getTestJsonFilePath("notjson.txt")}`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE"); expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
}); });
test("Some errors occured (exit code 1).", async () => { test("Fails if the collection file includes scripts with incorrect API usage and failed assertions", async () => {
const args = `test ${getTestJsonFilePath("fails.json")}`; const args = `test ${getTestJsonFilePath("fails-coll.json", "collection")}`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).not.toBeNull(); expect(error).not.toBeNull();
@@ -65,28 +64,39 @@ describe("Test 'hopp test <file>' command:", () => {
code: 1, code: 1,
}); });
}); });
});
test("No errors occured (exit code 0).", async () => { test("Successfully processes a supplied collection export file of the expected format", async () => {
const args = `test ${getTestJsonFilePath("passes.json")}`; const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Supports inheriting headers and authorization set at the root collection", async () => { test("Successfully inherits headers and authorization set at the root collection", async () => {
const args = `test ${getTestJsonFilePath("collection-level-headers-auth.json")}`; const args = `test ${getTestJsonFilePath(
"collection-level-headers-auth-coll.json", "collection"
)}`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}) });
test("Persists environment variables set in the pre-request script for consumption in the test script", async () => {
const args = `test ${getTestJsonFilePath(
"pre-req-script-env-var-persistence-coll.json", "collection"
)}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
}); });
describe("Test 'hopp test <file> --env <file>' command:", () => { describe("Test `hopp test <file> --env <file>` command:", () => {
const VALID_TEST_ARGS = `test ${getTestJsonFilePath( describe("Supplied environment export file validations", () => {
"passes.json" const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
)}`;
test("No env file path provided.", async () => { test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => {
const args = `${VALID_TEST_ARGS} --env`; const args = `${VALID_TEST_ARGS} --env`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -94,15 +104,17 @@ describe("Test 'hopp test <file> --env <file>' command:", () => {
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("ENV file not JSON type.", async () => { test("Errors with the code `INVALID_FILE_TYPE` if the supplied environment export file doesn't end with the `.json` extension", async () => {
const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath("notjson.txt")}`; const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath(
"notjson-coll.txt", "collection"
)}`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE"); expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
}); });
test("ENV file not found.", async () => { test("Errors with the code `FILE_NOT_FOUND` if the supplied environment export file doesn't exist", async () => {
const args = `${VALID_TEST_ARGS} --env notfound.json`; const args = `${VALID_TEST_ARGS} --env notfound.json`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -110,31 +122,128 @@ describe("Test 'hopp test <file> --env <file>' command:", () => {
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND"); expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
}); });
test("No errors occured (exit code 0).", async () => { test("Errors with the code `MALFORMED_ENV_FILE` on supplying a malformed environment export file", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json"); const ENV_PATH = getTestJsonFilePath("malformed-envs.json", "environment");
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json"); const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`;
const { stderr } = await runCLI(args);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("MALFORMED_ENV_FILE");
});
test("Errors with the code `BULK_ENV_FILE` on supplying an environment export file based on the bulk environment export format", async () => {
const ENV_PATH = getTestJsonFilePath("bulk-envs.json", "environment");
const args = `${VALID_TEST_ARGS} --env ${ENV_PATH}`;
const { stderr } = await runCLI(args);
const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("BULK_ENV_FILE");
});
});
test("Successfully resolves values from the supplied environment export file", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests-coll.json", "collection");
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment");
const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; const args = `test ${TESTS_PATH} --env ${ENV_PATH}`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Correctly resolves environment variables referenced in the request body", async () => { test("Successfully resolves environment variables referenced in the request body", async () => {
const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json"); const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json", "collection");
const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json"); const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json", "environment");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`; const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Works with shorth `-e` flag", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests-coll.json", "collection");
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json", "environment");
const args = `test ${TESTS_PATH} -e ${ENV_PATH}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
describe("Secret environment variables", () => {
jest.setTimeout(10000);
// Reads secret environment values from system environment
test("Successfully picks the values for secret environment variables from `process.env` and persists the variables set from the pre-request script", async () => {
const env = {
...process.env,
secretBearerToken: "test-token",
secretBasicAuthUsername: "test-user",
secretBasicAuthPassword: "test-pass",
secretQueryParamValue: "secret-query-param-value",
secretBodyValue: "secret-body-value",
secretHeaderValue: "secret-header-value",
};
const COLL_PATH = getTestJsonFilePath("secret-envs-coll.json", "collection");
const ENVS_PATH = getTestJsonFilePath("secret-envs.json", "environment");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error, stdout } = await runCLI(args, { env });
expect(stdout).toContain(
"https://httpbin.org/basic-auth/*********/*********"
);
expect(error).toBeNull();
});
// Prefers values specified in the environment export file over values set in the system environment
test("Successfully picks the values for secret environment variables set directly in the environment export file and persists the environment variables set from the pre-request script", async () => {
const COLL_PATH = getTestJsonFilePath("secret-envs-coll.json", "collection");
const ENVS_PATH = getTestJsonFilePath("secret-supplied-values-envs.json", "environment");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error, stdout } = await runCLI(args);
expect(stdout).toContain(
"https://httpbin.org/basic-auth/*********/*********"
);
expect(error).toBeNull();
});
// Values set from the scripting context takes the highest precedence
test("Setting values for secret environment variables from the pre-request script overrides values set at the supplied environment export file", async () => {
const COLL_PATH = getTestJsonFilePath(
"secret-envs-persistence-coll.json", "collection"
);
const ENVS_PATH = getTestJsonFilePath("secret-supplied-values-envs.json", "environment");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error, stdout } = await runCLI(args);
expect(stdout).toContain(
"https://httpbin.org/basic-auth/*********/*********"
);
expect(error).toBeNull();
});
test("Persists secret environment variable values set from the pre-request script for consumption in the request and post-request script context", async () => {
const COLL_PATH = getTestJsonFilePath(
"secret-envs-persistence-scripting-coll.json", "collection"
);
const ENVS_PATH = getTestJsonFilePath(
"secret-envs-persistence-scripting-envs.json", "environment"
);
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
});
}); });
describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => { describe("Test `hopp test <file> --delay <delay_in_ms>` command:", () => {
const VALID_TEST_ARGS = `test ${getTestJsonFilePath( const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
"passes.json"
)}`;
test("No value passed to delay flag.", async () => { test("Errors with the code `INVALID_ARGUMENT` on not supplying a delay value", async () => {
const args = `${VALID_TEST_ARGS} --delay`; const args = `${VALID_TEST_ARGS} --delay`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -142,7 +251,7 @@ describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Invalid value passed to delay flag.", async () => { test("Errors with the code `INVALID_ARGUMENT` on supplying an invalid delay value", async () => {
const args = `${VALID_TEST_ARGS} --delay 'NaN'`; const args = `${VALID_TEST_ARGS} --delay 'NaN'`;
const { stderr } = await runCLI(args); const { stderr } = await runCLI(args);
@@ -150,10 +259,17 @@ describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Valid value passed to delay flag.", async () => { test("Successfully performs delayed request execution for a valid delay value", async () => {
const args = `${VALID_TEST_ARGS} --delay 1`; const args = `${VALID_TEST_ARGS} --delay 1`;
const { error } = await runCLI(args); const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Works with the short `-d` flag", async () => {
const args = `${VALID_TEST_ARGS} -d 1`;
const { error } = await runCLI(args);
expect(error).toBeNull();
});
}); });

View File

@@ -17,7 +17,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestD", "name": "RequestD",
"params": [], "params": [],
@@ -40,7 +40,8 @@
"body": { "body": {
"contentType": null, "contentType": null,
"body": null "body": null
} },
"requestVariables": []
} }
], ],
"auth": { "auth": {
@@ -52,7 +53,7 @@
], ],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestC", "name": "RequestC",
"params": [], "params": [],
@@ -67,7 +68,8 @@
"body": { "body": {
"contentType": null, "contentType": null,
"body": null "body": null
} },
"requestVariables": []
} }
], ],
"auth": { "auth": {
@@ -88,7 +90,7 @@
], ],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestB", "name": "RequestB",
"params": [], "params": [],
@@ -104,6 +106,7 @@
"contentType": null, "contentType": null,
"body": null "body": null
}, },
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv" "id": "clpttpdq00003qp16kut6doqv"
} }
], ],
@@ -116,7 +119,7 @@
], ],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestA", "name": "RequestA",
"params": [], "params": [],
@@ -132,6 +135,7 @@
"contentType": null, "contentType": null,
"body": null "body": null
}, },
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv" "id": "clpttpdq00003qp16kut6doqv"
} }
], ],
@@ -158,7 +162,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestB", "name": "RequestB",
"params": [], "params": [],
@@ -174,6 +178,7 @@
"contentType": null, "contentType": null,
"body": null "body": null
}, },
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv" "id": "clpttpdq00003qp16kut6doqv"
} }
], ],
@@ -186,7 +191,7 @@
], ],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"name": "RequestA", "name": "RequestA",
"params": [], "params": [],
@@ -202,6 +207,7 @@
"contentType": null, "contentType": null,
"body": null "body": null
}, },
"requestVariables": [],
"id": "clpttpdq00003qp16kut6doqv" "id": "clpttpdq00003qp16kut6doqv"
} }
], ],

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "<<URL>>", "endpoint": "<<URL>>",
"name": "test1", "name": "test1",
"params": [], "params": [],
@@ -16,7 +16,8 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n \"<<BODY_KEY>>\":\"<<BODY_VALUE>>\"\n}" "body": "{\n \"<<BODY_KEY>>\":\"<<BODY_VALUE>>\"\n}"
} },
"requestVariables": []
} }
] ]
} }

View File

@@ -5,7 +5,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "", "name": "",
"params": [], "params": [],
@@ -23,10 +23,11 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
} },
"requestVariables": [],
}, },
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.dio/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.dio/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -44,7 +45,8 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
} },
"requestVariables": []
} }
] ]
} }

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": "requests":
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "fail", "name": "fail",
"params": [], "params": [],
@@ -22,10 +22,11 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
} },
"requestVariables": [],
}, },
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -43,7 +44,8 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
} },
"requestVariables": []
} }
] ]
} }

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": "requests":
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "fail", "name": "fail",
"params": [], "params": [],
@@ -22,7 +22,8 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
} },
"requestVariables": []
} }
] ]
} }

View File

@@ -5,7 +5,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
"name": "", "name": "",
"params": [], "params": [],
@@ -23,10 +23,11 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
} },
"requestVariables": []
}, },
{ {
"v": "1", "v": "2",
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>", "endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
"name": "success", "name": "success",
"params": [], "params": [],
@@ -44,7 +45,8 @@
"body": { "body": {
"contentType": "application/json", "contentType": "application/json",
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}" "body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
} },
"requestVariables": []
} }
] ]
} }

View File

@@ -0,0 +1,22 @@
{
"v": 2,
"name": "pre-req-script-env-var-persistence-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "sample-req",
"method": "GET",
"params": [],
"headers": [],
"endpoint": "https://echo.hoppscotch.io",
"testScript": "pw.expect(pw.env.get(\"variable\")).toBe(\"value\")",
"preRequestScript": "pw.env.set(\"variable\", \"value\");",
"requestVariables": []
}
],
"auth": { "authType": "inherit", "authActive": true },
"headers": []
}

View File

@@ -4,7 +4,7 @@
"folders": [], "folders": [],
"requests": [ "requests": [
{ {
"v": "1", "v": "2",
"name": "test-request", "name": "test-request",
"endpoint": "https://echo.hoppscotch.io", "endpoint": "https://echo.hoppscotch.io",
"method": "POST", "method": "POST",
@@ -19,7 +19,8 @@
"body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}" "body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}"
}, },
"preRequestScript": "", "preRequestScript": "",
"testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully resolves environments recursively\", ()=> {\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n});\n\npw.test(\"Successfully resolves environments referenced in the request body\", () => {\n const expectedId = \"7\"\n const expectedFirstName = \"John\"\n const expectedLastName = \"Doe\"\n const expectedFullName = `${expectedFirstName} ${expectedLastName}`\n const expectedGreetText = `Hello, ${expectedFullName}`\n\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n\n const { id, firstName, lastName, fullName, greetText } = JSON.parse(pw.response.body.data)\n\n pw.expect(id).toBe(expectedId)\n pw.expect(expectedFirstName).toBe(firstName)\n pw.expect(expectedLastName).toBe(lastName)\n pw.expect(fullName).toBe(expectedFullName)\n pw.expect(greetText).toBe(expectedGreetText)\n});" "testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully resolves environments recursively\", ()=> {\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n});\n\npw.test(\"Successfully resolves environments referenced in the request body\", () => {\n const expectedId = \"7\"\n const expectedFirstName = \"John\"\n const expectedLastName = \"Doe\"\n const expectedFullName = `${expectedFirstName} ${expectedLastName}`\n const expectedGreetText = `Hello, ${expectedFullName}`\n\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n\n const { id, firstName, lastName, fullName, greetText } = JSON.parse(pw.response.body.data)\n\n pw.expect(id).toBe(expectedId)\n pw.expect(expectedFirstName).toBe(firstName)\n pw.expect(expectedLastName).toBe(lastName)\n pw.expect(fullName).toBe(expectedFullName)\n pw.expect(greetText).toBe(expectedGreetText)\n});",
"requestVariables": []
} }
], ],
"auth": { "auth": {

View File

@@ -0,0 +1,113 @@
{
"v": 2,
"name": "secret-envs-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-headers",
"method": "GET",
"params": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"requestVariables": [],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.get(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.get(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "const secretHeaderValueFromPreReqScript = pw.env.get(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": {
"body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}",
"contentType": "application/json"
},
"name": "test-secret-body",
"method": "POST",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-query-params",
"method": "GET",
"params": [
{
"key": "secretQueryParamKey",
"value": "<<secretQueryParamValue>>",
"active": true
}
],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/get",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "basic",
"password": "<<secretBasicAuthPassword>>",
"username": "<<secretBasicAuthUsername>>",
"authActive": true
},
"body": { "body": null, "contentType": null },
"name": "test-secret-basic-auth",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": ""
},
{
"v": "2",
"auth": {
"token": "<<secretBearerToken>>",
"authType": "bearer",
"password": "testpassword",
"username": "testuser",
"authActive": true
},
"body": { "body": null, "contentType": null },
"name": "test-secret-bearer-auth",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.get(\"secretBearerToken\")\n const preReqSecretBearerToken = pw.env.get(\"preReqSecretBearerToken\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "const secretBearerToken = pw.env.get(\"secretBearerToken\")\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
},
{
"v": "2",
"auth": { "authType": "none", "authActive": true },
"body": { "body": null, "contentType": null },
"name": "test-secret-fallback",
"method": "GET",
"params": [],
"headers": [],
"requestVariables": [],
"endpoint": "<<baseURL>>",
"testScript": "pw.test(\"Returns an empty string if the value for a secret environment variable is not found in the system environment\", () => {\n pw.expect(pw.env.get(\"nonExistentValueInSystemEnv\")).toBe(\"\")\n})",
"preRequestScript": ""
}
],
"auth": { "authType": "inherit", "authActive": false },
"headers": []
}

View File

@@ -0,0 +1,149 @@
{
"v": 2,
"name": "secret-envs-setters-coll",
"folders": [],
"requests": [
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-headers",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Successfully parses secret variable holding the header value\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-headers-overrides",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [
{
"key": "Secret-Header-Key",
"value": "<<secretHeaderValue>>",
"active": true
}
],
"endpoint": "<<baseURL>>/headers",
"testScript": "pw.test(\"Value set at the pre-request script takes precedence\", () => {\n const secretHeaderValue = pw.env.getResolve(\"secretHeaderValue\")\n pw.expect(secretHeaderValue).toBe(\"secret-header-value-overriden\")\n \n if (secretHeaderValue) {\n pw.expect(pw.response.body.headers[\"Secret-Header-Key\"]).toBe(secretHeaderValue)\n }\n\n pw.expect(pw.env.getResolve(\"secretHeaderValueFromPreReqScript\")).toBe(\"secret-header-value-overriden\")\n})",
"preRequestScript": "pw.env.set(\"secretHeaderValue\", \"secret-header-value-overriden\")\n\nconst secretHeaderValueFromPreReqScript = pw.env.getResolve(\"secretHeaderValue\")\npw.env.set(\"secretHeaderValueFromPreReqScript\", secretHeaderValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": "{\n \"secretBodyKey\": \"<<secretBodyValue>>\"\n}",
"contentType": "application/json"
},
"name": "test-secret-body",
"method": "POST",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/post",
"testScript": "pw.test(\"Successfully parses secret variable holding the request body value\", () => {\n const secretBodyValue = pw.env.get(\"secretBodyValue\")\n pw.expect(secretBodyValue).toBe(\"secret-body-value\")\n \n if (secretBodyValue) {\n pw.expect(pw.response.body.json.secretBodyKey).toBe(secretBodyValue)\n }\n\n pw.expect(pw.env.get(\"secretBodyValueFromPreReqScript\")).toBe(\"secret-body-value\")\n})",
"preRequestScript": "const secretBodyValue = pw.env.get(\"secretBodyValue\")\n\nif (!secretBodyValue) { \n pw.env.set(\"secretBodyValue\", \"secret-body-value\")\n}\n\nconst secretBodyValueFromPreReqScript = pw.env.get(\"secretBodyValue\")\npw.env.set(\"secretBodyValueFromPreReqScript\", secretBodyValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-query-params",
"method": "GET",
"params": [
{
"key": "secretQueryParamKey",
"value": "<<secretQueryParamValue>>",
"active": true
}
],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/get",
"testScript": "pw.test(\"Successfully parses secret variable holding the query param value\", () => {\n const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n pw.expect(secretQueryParamValue).toBe(\"secret-query-param-value\")\n \n if (secretQueryParamValue) {\n pw.expect(pw.response.body.args.secretQueryParamKey).toBe(secretQueryParamValue)\n }\n\n pw.expect(pw.env.get(\"secretQueryParamValueFromPreReqScript\")).toBe(\"secret-query-param-value\")\n})",
"preRequestScript": "const secretQueryParamValue = pw.env.get(\"secretQueryParamValue\")\n\nif (!secretQueryParamValue) {\n pw.env.set(\"secretQueryParamValue\", \"secret-query-param-value\")\n}\n\nconst secretQueryParamValueFromPreReqScript = pw.env.get(\"secretQueryParamValue\")\npw.env.set(\"secretQueryParamValueFromPreReqScript\", secretQueryParamValueFromPreReqScript)"
},
{
"v": "2",
"auth": {
"authType": "basic",
"password": "<<secretBasicAuthPassword>>",
"username": "<<secretBasicAuthUsername>>",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-basic-auth",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/basic-auth/<<secretBasicAuthUsername>>/<<secretBasicAuthPassword>>",
"testScript": "pw.test(\"Successfully parses secret variables holding basic auth credentials\", () => {\n\tconst secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n \tconst secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\n pw.expect(secretBasicAuthUsername).toBe(\"test-user\")\n pw.expect(secretBasicAuthPassword).toBe(\"test-pass\")\n\n if (secretBasicAuthUsername && secretBasicAuthPassword) {\n const { authenticated, user } = pw.response.body\n pw.expect(authenticated).toBe(true)\n pw.expect(user).toBe(secretBasicAuthUsername)\n }\n});",
"preRequestScript": "let secretBasicAuthUsername = pw.env.get(\"secretBasicAuthUsername\")\n\nlet secretBasicAuthPassword = pw.env.get(\"secretBasicAuthPassword\")\n\nif (!secretBasicAuthUsername) {\n pw.env.set(\"secretBasicAuthUsername\", \"test-user\")\n}\n\nif (!secretBasicAuthPassword) {\n pw.env.set(\"secretBasicAuthPassword\", \"test-pass\")\n}"
},
{
"v": "2",
"auth": {
"token": "<<secretBearerToken>>",
"authType": "bearer",
"password": "testpassword",
"username": "testuser",
"authActive": true
},
"body": {
"body": null,
"contentType": null
},
"name": "test-secret-bearer-auth",
"method": "GET",
"params": [],
"requestVariables": [],
"headers": [],
"endpoint": "<<baseURL>>/bearer",
"testScript": "pw.test(\"Successfully parses secret variable holding the bearer token\", () => {\n const secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n const preReqSecretBearerToken = pw.env.resolve(\"<<preReqSecretBearerToken>>\")\n\n pw.expect(secretBearerToken).toBe(\"test-token\")\n\n if (secretBearerToken) { \n pw.expect(pw.response.body.token).toBe(secretBearerToken)\n pw.expect(preReqSecretBearerToken).toBe(\"test-token\")\n }\n});",
"preRequestScript": "let secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n\nif (!secretBearerToken) {\n pw.env.set(\"secretBearerToken\", \"test-token\")\n secretBearerToken = pw.env.resolve(\"<<secretBearerToken>>\")\n}\n\npw.env.set(\"preReqSecretBearerToken\", secretBearerToken)"
}
],
"auth": {
"authType": "inherit",
"authActive": false
},
"headers": []
}

View File

@@ -0,0 +1,31 @@
{
"v": 2,
"name": "secret-envs-persistence-scripting-req",
"folders": [],
"requests": [
{
"v": "2",
"endpoint": "https://httpbin.org/post",
"name": "req",
"params": [],
"headers": [
{
"active": true,
"key": "Custom-Header",
"value": "<<customHeaderValueFromSecretVar>>"
}
],
"method": "POST",
"auth": { "authType": "none", "authActive": true },
"preRequestScript": "pw.env.set(\"preReqVarOne\", \"pre-req-value-one\")\n\npw.env.set(\"preReqVarTwo\", \"pre-req-value-two\")\n\npw.env.set(\"customHeaderValueFromSecretVar\", \"custom-header-secret-value\")\n\npw.env.set(\"customBodyValue\", \"custom-body-value\")",
"testScript": "pw.test(\"Secret environment value set from the pre-request script takes precedence\", () => {\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(\"pre-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the pre-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request headers that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.headers[\"Custom-Header\"]).toBe(\"custom-header-secret-value\")\n})\n\npw.test(\"Successfully resolves secret variable values referred in request body that are set in pre-request sccript\", () => {\n pw.expect(pw.response.body.json.key).toBe(\"custom-body-value\")\n})\n\npw.test(\"Secret environment variable set from the post-request script takes precedence\", () => {\n pw.env.set(\"postReqVarOne\", \"post-req-value-one\")\n pw.expect(pw.env.get(\"postReqVarOne\")).toBe(\"post-req-value-one\")\n})\n\npw.test(\"Successfully sets initial value for the secret variable from the post-request script\", () => {\n pw.env.set(\"postReqVarTwo\", \"post-req-value-two\")\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(\"post-req-value-two\")\n})\n\npw.test(\"Successfully removes environment variables via the pw.env.unset method\", () => {\n pw.env.unset(\"preReqVarOne\")\n pw.env.unset(\"postReqVarTwo\")\n\n pw.expect(pw.env.get(\"preReqVarOne\")).toBe(undefined)\n pw.expect(pw.env.get(\"postReqVarTwo\")).toBe(undefined)\n})",
"body": {
"contentType": "application/json",
"body": "{\n \"key\": \"<<customBodyValue>>\"\n}"
},
"requestVariables": []
}
],
"auth": { "authType": "inherit", "authActive": false },
"headers": []
}

View File

@@ -0,0 +1,32 @@
[
{
"v": 0,
"name": "Env-I",
"variables": [
{
"key": "firstName",
"value": "John"
},
{
"key": "lastName",
"value": "Doe"
}
]
},
{
"v": 1,
"id": "2",
"name": "Env-II",
"variables": [
{
"key": "baseUrl",
"value": "https://echo.hoppscotch.io",
"secret": false
},
{
"key": "secretVar",
"secret": true
}
]
}
]

View File

@@ -0,0 +1,16 @@
{
"id": 123,
"v": "1",
"name": "secret-envs",
"values": [
{
"key": "secretVar",
"secret": true
},
{
"key": "regularVar",
"secret": false,
"value": "regular-variable"
}
]
}

View File

@@ -1,4 +1,5 @@
{ {
"v": 0,
"name": "Response body sample", "name": "Response body sample",
"variables": [ "variables": [
{ {

View File

@@ -0,0 +1,27 @@
{
"v": 1,
"id": "2",
"name": "secret-envs-persistence-scripting-envs",
"variables": [
{
"key": "preReqVarOne",
"secret": true
},
{
"key": "preReqVarTwo",
"secret": true
},
{
"key": "postReqVarOne",
"secret": true
},
{
"key": "preReqVarTwo",
"secret": true
},
{
"key": "customHeaderValueFromSecretVar",
"secret": true
}
]
}

View File

@@ -0,0 +1,40 @@
{
"id": "2",
"v": 1,
"name": "secret-envs",
"variables": [
{
"key": "secretBearerToken",
"secret": true
},
{
"key": "secretBasicAuthUsername",
"secret": true
},
{
"key": "secretBasicAuthPassword",
"secret": true
},
{
"key": "secretQueryParamValue",
"secret": true
},
{
"key": "secretBodyValue",
"secret": true
},
{
"key": "secretHeaderValue",
"secret": true
},
{
"key": "nonExistentValueInSystemEnv",
"secret": true
},
{
"key": "baseURL",
"value": "https://httpbin.org",
"secret": false
}
]
}

View File

@@ -0,0 +1,46 @@
{
"v": 1,
"id": "2",
"name": "secret-values-envs",
"variables": [
{
"key": "secretBearerToken",
"value": "test-token",
"secret": true
},
{
"key": "secretBasicAuthUsername",
"value": "test-user",
"secret": true
},
{
"key": "secretBasicAuthPassword",
"value": "test-pass",
"secret": true
},
{
"key": "secretQueryParamValue",
"value": "secret-query-param-value",
"secret": true
},
{
"key": "secretBodyValue",
"value": "secret-body-value",
"secret": true
},
{
"key": "secretHeaderValue",
"value": "secret-header-value",
"secret": true
},
{
"key": "nonExistentValueInSystemEnv",
"secret": true
},
{
"key": "baseURL",
"value": "https://httpbin.org",
"secret": false
}
]
}

View File

@@ -3,13 +3,13 @@ import { resolve } from "path";
import { ExecResponse } from "./types"; import { ExecResponse } from "./types";
export const runCLI = (args: string): Promise<ExecResponse> => export const runCLI = (args: string, options = {}): Promise<ExecResponse> =>
{ {
const CLI_PATH = resolve(__dirname, "../../bin/hopp"); const CLI_PATH = resolve(__dirname, "../../bin/hopp");
const command = `node ${CLI_PATH} ${args}` const command = `node ${CLI_PATH} ${args}`
return new Promise((resolve) => return new Promise((resolve) =>
exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr })) exec(command, options, (error, stdout, stderr) => resolve({ error, stdout, stderr }))
); );
} }
@@ -25,7 +25,12 @@ export const getErrorCode = (out: string) => {
return ansiTrimmedStr.split(" ")[0]; return ansiTrimmedStr.split(" ")[0];
}; };
export const getTestJsonFilePath = (file: string) => { export const getTestJsonFilePath = (file: string, kind: "collection" | "environment") => {
const filePath = resolve(__dirname, `../../src/__tests__/samples/${file}`); const kindDir = {
collection: "collections",
environment: "environments",
}[kind];
const filePath = resolve(__dirname, `../../src/__tests__/samples/${kindDir}/${file}`);
return filePath; return filePath;
}; };

View File

@@ -1,5 +1,5 @@
import chalk from "chalk"; import chalk from "chalk";
import { program } from "commander"; import { Command } from "commander";
import * as E from "fp-ts/Either"; import * as E from "fp-ts/Either";
import { version } from "../package.json"; import { version } from "../package.json";
import { test } from "./commands/test"; import { test } from "./commands/test";
@@ -20,6 +20,8 @@ const CLI_AFTER_ALL_TXT = `\nFor more help, head on to ${accent(
"https://docs.hoppscotch.io/documentation/clients/cli" "https://docs.hoppscotch.io/documentation/clients/cli"
)}`; )}`;
const program = new Command()
program program
.name("hopp") .name("hopp")
.version(version, "-v, --ver", "see the current version of hopp-cli") .version(version, "-v, --ver", "see the current version of hopp-cli")

View File

@@ -21,6 +21,7 @@ export interface RequestStack {
*/ */
export interface RequestConfig extends AxiosRequestConfig { export interface RequestConfig extends AxiosRequestConfig {
supported: boolean; supported: boolean;
displayUrl?: string
} }
export interface EffectiveHoppRESTRequest extends HoppRESTRequest { export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
@@ -30,6 +31,7 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
* This contains path, params and environment variables all applied to it * This contains path, params and environment variables all applied to it
*/ */
effectiveFinalURL: string; effectiveFinalURL: string;
effectiveFinalDisplayURL?: string;
effectiveFinalHeaders: { key: string; value: string; active: boolean }[]; effectiveFinalHeaders: { key: string; value: string; active: boolean }[];
effectiveFinalParams: { key: string; value: string; active: boolean }[]; effectiveFinalParams: { key: string; value: string; active: boolean }[];
effectiveFinalBody: FormData | string | null; effectiveFinalBody: FormData | string | null;

View File

@@ -1,34 +1,42 @@
import { Environment } from "@hoppscotch/data";
import { entityReference } from "verzod";
import { z } from "zod";
import { error } from "../../types/errors"; import { error } from "../../types/errors";
import { import {
HoppEnvs,
HoppEnvPair,
HoppEnvKeyPairObject, HoppEnvKeyPairObject,
HoppEnvExportObject, HoppEnvPair,
HoppBulkEnvExportObject, HoppEnvs
} from "../../types/request"; } from "../../types/request";
import { readJsonFile } from "../../utils/mutators"; import { readJsonFile } from "../../utils/mutators";
/** /**
* Parses env json file for given path and validates the parsed env json object. * Parses env json file for given path and validates the parsed env json object
* @param path Path of env.json file to be parsed. * @param path Path of env.json file to be parsed
* @returns For successful parsing we get HoppEnvs object. * @returns For successful parsing we get HoppEnvs object
*/ */
export async function parseEnvsData(path: string) { export async function parseEnvsData(path: string) {
const contents = await readJsonFile(path); const contents = await readJsonFile(path);
const envPairs: Array<HoppEnvPair> = []; const envPairs: Array<Environment["variables"][number] | HoppEnvPair> = [];
const HoppEnvKeyPairResult = HoppEnvKeyPairObject.safeParse(contents);
const HoppEnvExportObjectResult = HoppEnvExportObject.safeParse(contents);
const HoppBulkEnvExportObjectResult =
HoppBulkEnvExportObject.safeParse(contents);
// CLI doesnt support bulk environments export. // The legacy key-value pair format that is still supported
// Hence we check for this case and throw an error if it matches the format. const HoppEnvKeyPairResult = HoppEnvKeyPairObject.safeParse(contents);
// Shape of the single environment export object that is exported from the app
const HoppEnvExportObjectResult = Environment.safeParse(contents);
// Shape of the bulk environment export object that is exported from the app
const HoppBulkEnvExportObjectResult = z.array(entityReference(Environment)).safeParse(contents)
// CLI doesnt support bulk environments export
// Hence we check for this case and throw an error if it matches the format
if (HoppBulkEnvExportObjectResult.success) { if (HoppBulkEnvExportObjectResult.success) {
throw error({ code: "BULK_ENV_FILE", path, data: error }); throw error({ code: "BULK_ENV_FILE", path, data: error });
} }
// Checks if the environment file is of the correct format. // Checks if the environment file is of the correct format
// If it doesnt match either of them, we throw an error. // If it doesnt match either of them, we throw an error
if (!(HoppEnvKeyPairResult.success || HoppEnvExportObjectResult.success)) { if (!HoppEnvKeyPairResult.success && HoppEnvExportObjectResult.type === "err") {
throw error({ code: "MALFORMED_ENV_FILE", path, data: error }); throw error({ code: "MALFORMED_ENV_FILE", path, data: error });
} }
@@ -36,8 +44,8 @@ export async function parseEnvsData(path: string) {
for (const [key, value] of Object.entries(HoppEnvKeyPairResult.data)) { for (const [key, value] of Object.entries(HoppEnvKeyPairResult.data)) {
envPairs.push({ key, value }); envPairs.push({ key, value });
} }
} else if (HoppEnvExportObjectResult.success) { } else if (HoppEnvExportObjectResult.type === "ok") {
envPairs.push(...HoppEnvExportObjectResult.data.variables); envPairs.push(...HoppEnvExportObjectResult.value.variables);
} }
return <HoppEnvs>{ global: [], selected: envPairs }; return <HoppEnvs>{ global: [], selected: envPairs };

View File

@@ -1,31 +1,18 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import { z } from "zod";
import { TestReport } from "../interfaces/response"; import { TestReport } from "../interfaces/response";
import { HoppCLIError } from "./errors"; import { HoppCLIError } from "./errors";
import { z } from "zod";
export type FormDataEntry = { export type FormDataEntry = {
key: string; key: string;
value: string | Blob; value: string | Blob;
}; };
export type HoppEnvPair = { key: string; value: string }; export type HoppEnvPair = Environment["variables"][number];
export const HoppEnvKeyPairObject = z.record(z.string(), z.string()); export const HoppEnvKeyPairObject = z.record(z.string(), z.string());
// Shape of the single environment export object that is exported from the app.
export const HoppEnvExportObject = z.object({
name: z.string(),
variables: z.array(
z.object({
key: z.string(),
value: z.string(),
})
),
});
// Shape of the bulk environment export object that is exported from the app.
export const HoppBulkEnvExportObject = z.array(HoppEnvExportObject);
export type HoppEnvs = { export type HoppEnvs = {
global: HoppEnvPair[]; global: HoppEnvPair[];
selected: HoppEnvPair[]; selected: HoppEnvPair[];

View File

@@ -1,9 +1,9 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import { bold } from "chalk"; import chalk from "chalk";
import { log } from "console"; import { log } from "console";
import * as A from "fp-ts/Array"; import * as A from "fp-ts/Array";
import { pipe } from "fp-ts/function"; import { pipe } from "fp-ts/function";
import round from "lodash/round"; import { round } from "lodash-es";
import { CollectionRunnerParam } from "../types/collections"; import { CollectionRunnerParam } from "../types/collections";
import { import {
@@ -68,7 +68,7 @@ export const collectionsRunner = async (
}; };
// Request processing initiated message. // Request processing initiated message.
log(WARN(`\nRunning: ${bold(requestPath)}`)); log(WARN(`\nRunning: ${chalk.bold(requestPath)}`));
// Processing current request. // Processing current request.
const result = await processRequest(processRequestParams)(); const result = await processRequest(processRequestParams)();

View File

@@ -1,4 +1,4 @@
import { bold } from "chalk"; import chalk from "chalk";
import { groupEnd, group, log } from "console"; import { groupEnd, group, log } from "console";
import { handleError } from "../handlers/error"; import { handleError } from "../handlers/error";
import { RequestConfig } from "../interfaces/request"; import { RequestConfig } from "../interfaces/request";
@@ -120,7 +120,7 @@ export const printErrorsReport = (
errorsReport: HoppCLIError[] errorsReport: HoppCLIError[]
) => { ) => {
if (errorsReport.length > 0) { if (errorsReport.length > 0) {
const REPORTED_ERRORS_TITLE = FAIL(`\n${bold(path)} reported errors:`); const REPORTED_ERRORS_TITLE = FAIL(`\n${chalk.bold(path)} reported errors:`);
group(REPORTED_ERRORS_TITLE); group(REPORTED_ERRORS_TITLE);
for (const errorReport of errorsReport) { for (const errorReport of errorsReport) {
@@ -143,7 +143,7 @@ export const printFailedTestsReport = (
// Only printing test-reports with failed test-cases. // Only printing test-reports with failed test-cases.
if (failedTestsReport.length > 0) { if (failedTestsReport.length > 0) {
const FAILED_TESTS_PATH = FAIL(`\n${bold(path)} failed tests:`); const FAILED_TESTS_PATH = FAIL(`\n${chalk.bold(path)} failed tests:`);
group(FAILED_TESTS_PATH); group(FAILED_TESTS_PATH);
for (const failedTestReport of failedTestsReport) { for (const failedTestReport of failedTestsReport) {
@@ -176,7 +176,7 @@ export const printRequestRunner = {
*/ */
start: (requestConfig: RequestConfig) => { start: (requestConfig: RequestConfig) => {
const METHOD = BG_INFO(` ${requestConfig.method} `); const METHOD = BG_INFO(` ${requestConfig.method} `);
const ENDPOINT = requestConfig.url; const ENDPOINT = requestConfig.displayUrl || requestConfig.url;
process.stdout.write(`${METHOD} ${ENDPOINT}`); process.stdout.write(`${METHOD} ${ENDPOINT}`);
}, },

View File

@@ -1,4 +1,4 @@
import { clone } from "lodash"; import { clone } from "lodash-es";
/** /**
* Sorts the array based on the sort func. * Sorts the array based on the sort func.

View File

@@ -11,7 +11,7 @@ import * as E from "fp-ts/Either";
import * as S from "fp-ts/string"; import * as S from "fp-ts/string";
import * as O from "fp-ts/Option"; import * as O from "fp-ts/Option";
import { error } from "../types/errors"; import { error } from "../types/errors";
import round from "lodash/round"; import { round } from "lodash-es";
import { DEFAULT_DURATION_PRECISION } from "./constants"; import { DEFAULT_DURATION_PRECISION } from "./constants";
/** /**

View File

@@ -36,7 +36,10 @@ import { toFormData } from "./mutators";
export const preRequestScriptRunner = ( export const preRequestScriptRunner = (
request: HoppRESTRequest, request: HoppRESTRequest,
envs: HoppEnvs envs: HoppEnvs
): TE.TaskEither<HoppCLIError, EffectiveHoppRESTRequest> => ): TE.TaskEither<
HoppCLIError,
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
> =>
pipe( pipe(
TE.of(request), TE.of(request),
TE.chain(({ preRequestScript }) => TE.chain(({ preRequestScript }) =>
@@ -68,7 +71,10 @@ export const preRequestScriptRunner = (
export function getEffectiveRESTRequest( export function getEffectiveRESTRequest(
request: HoppRESTRequest, request: HoppRESTRequest,
environment: Environment environment: Environment
): E.Either<HoppCLIError, EffectiveHoppRESTRequest> { ): E.Either<
HoppCLIError,
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
> {
const envVariables = environment.variables; const envVariables = environment.variables;
// Parsing final headers with applied ENVs. // Parsing final headers with applied ENVs.
@@ -162,12 +168,30 @@ export function getEffectiveRESTRequest(
} }
const effectiveFinalURL = _effectiveFinalURL.right; const effectiveFinalURL = _effectiveFinalURL.right;
// Secret environment variables referenced in the request endpoint should be masked
let effectiveFinalDisplayURL;
if (envVariables.some(({ secret }) => secret)) {
const _effectiveFinalDisplayURL = parseTemplateStringE(
request.endpoint,
envVariables,
true
);
if (E.isRight(_effectiveFinalDisplayURL)) {
effectiveFinalDisplayURL = _effectiveFinalDisplayURL.right;
}
}
return E.right({ return E.right({
effectiveRequest: {
...request, ...request,
effectiveFinalURL, effectiveFinalURL,
effectiveFinalDisplayURL,
effectiveFinalHeaders, effectiveFinalHeaders,
effectiveFinalParams, effectiveFinalParams,
effectiveFinalBody, effectiveFinalBody,
},
updatedEnvs: { global: [], selected: envVariables },
}); });
} }

View File

@@ -1,4 +1,4 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"; import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import axios, { Method } from "axios"; import axios, { Method } from "axios";
import * as A from "fp-ts/Array"; import * as A from "fp-ts/Array";
import * as E from "fp-ts/Either"; import * as E from "fp-ts/Either";
@@ -29,6 +29,38 @@ import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test";
// !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported // !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported
/**
* Processes given variable, which includes checking for secret variables
* and getting value from system environment
* @param variable Variable to be processed
* @returns Updated variable with value from system environment
*/
const processVariables = (variable: Environment["variables"][number]) => {
if (variable.secret) {
return {
...variable,
value:
"value" in variable ? variable.value : process.env[variable.key] || "",
}
}
return variable
}
/**
* Processes given envs, which includes processing each variable in global
* and selected envs
* @param envs Global + selected envs used by requests with in collection
* @returns Processed envs with each variable processed
*/
const processEnvs = (envs: HoppEnvs) => {
const processedEnvs = {
global: envs.global.map(processVariables),
selected: envs.selected.map(processVariables),
}
return processedEnvs
}
/** /**
* Transforms given request data to request-config used by request-runner to * Transforms given request data to request-config used by request-runner to
* perform HTTP request. * perform HTTP request.
@@ -38,6 +70,7 @@ import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test";
export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => { export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => {
const config: RequestConfig = { const config: RequestConfig = {
supported: true, supported: true,
displayUrl: req.effectiveFinalDisplayURL
}; };
const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest; const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest;
const reqParams = finalParams(req); const reqParams = finalParams(req);
@@ -221,9 +254,13 @@ export const processRequest =
effectiveFinalParams: [], effectiveFinalParams: [],
effectiveFinalURL: "", effectiveFinalURL: "",
}; };
let updatedEnvs = <HoppEnvs>{};
// Fetch values for secret environment variables from system environment
const processedEnvs = processEnvs(envs)
// Executing pre-request-script // Executing pre-request-script
const preRequestRes = await preRequestScriptRunner(request, envs)(); const preRequestRes = await preRequestScriptRunner(request, processedEnvs)();
if (E.isLeft(preRequestRes)) { if (E.isLeft(preRequestRes)) {
printPreRequestRunner.fail(); printPreRequestRunner.fail();
@@ -231,8 +268,8 @@ export const processRequest =
report.errors.push(preRequestRes.left); report.errors.push(preRequestRes.left);
report.result = report.result && false; report.result = report.result && false;
} else { } else {
// Updating effective-request // Updating effective-request and consuming updated envs after pre-request script execution
effectiveRequest = preRequestRes.right; ({ effectiveRequest, updatedEnvs } = preRequestRes.right);
} }
// Creating request-config for request-runner. // Creating request-config for request-runner.
@@ -270,7 +307,7 @@ export const processRequest =
const testScriptParams = getTestScriptParams( const testScriptParams = getTestScriptParams(
_requestRunnerRes, _requestRunnerRes,
request, request,
envs updatedEnvs
); );
// Executing test-runner. // Executing test-runner.

View File

@@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES6", "target": "ESNext",
"module": "commonjs", "module": "ESNext",
"outDir": ".", "outDir": ".",
"rootDir": ".", "rootDir": ".",
"strict": true, "strict": true,

View File

@@ -3,17 +3,14 @@ import { defineConfig } from "tsup";
export default defineConfig({ export default defineConfig({
entry: [ "./src/index.ts" ], entry: [ "./src/index.ts" ],
outDir: "./dist/", outDir: "./dist/",
format: ["cjs"], format: ["esm"],
platform: "node", platform: "node",
sourcemap: true, sourcemap: true,
bundle: true, bundle: true,
target: "node12", target: "esnext",
skipNodeModulesBundle: false, skipNodeModulesBundle: false,
esbuildOptions(options) { esbuildOptions(options) {
options.bundle = true options.bundle = true
}, },
noExternal: [
/\w+/
],
clean: true, clean: true,
}); });

View File

@@ -429,6 +429,11 @@ pre.ace_editor {
} }
} }
.splitpanes__pane {
@apply will-change-auto;
transform: translateZ(0);
}
.smart-splitter .splitpanes__splitter { .smart-splitter .splitpanes__splitter {
@apply relative; @apply relative;
@apply before:absolute; @apply before:absolute;
@@ -558,12 +563,22 @@ details[open] summary .indicator {
.env-highlight { .env-highlight {
@apply text-accentContrast; @apply text-accentContrast;
&.env-found { &.request-variable-highlight {
@apply bg-accentDark; @apply bg-amber-500;
@apply hover:bg-accent; @apply hover:bg-amber-600;
} }
&.env-not-found { &.environment-variable-highlight {
@apply bg-green-500;
@apply hover:bg-green-600;
}
&.global-variable-highlight {
@apply bg-blue-500;
@apply hover:bg-blue-600;
}
&.environment-not-found-highlight {
@apply bg-red-500; @apply bg-red-500;
@apply hover:bg-red-600; @apply hover:bg-red-600;
} }

View File

@@ -6,7 +6,7 @@
"choose_file": "选择文件", "choose_file": "选择文件",
"clear": "清除", "clear": "清除",
"clear_all": "全部清除", "clear_all": "全部清除",
"clear_history": "Clear all History", "clear_history": "清除全部历史记录",
"close": "关闭", "close": "关闭",
"connect": "连接", "connect": "连接",
"connecting": "连接中", "connecting": "连接中",
@@ -35,7 +35,7 @@
"prettify": "美化", "prettify": "美化",
"properties": "Properties", "properties": "Properties",
"remove": "移除", "remove": "移除",
"rename": "Rename", "rename": "重命名",
"restore": "恢复", "restore": "恢复",
"save": "保存", "save": "保存",
"scroll_to_bottom": "滚动至底部", "scroll_to_bottom": "滚动至底部",
@@ -86,8 +86,8 @@
"search": "搜索", "search": "搜索",
"share": "分享", "share": "分享",
"shortcuts": "快捷方式", "shortcuts": "快捷方式",
"social_description": "Follow us on social media to stay updated with the latest news, updates and releases.", "social_description": "在社交媒体上关注我们,了解最新新闻、更新和发布。",
"social_links": "Social links", "social_links": "社交媒体链接",
"spotlight": "聚光灯", "spotlight": "聚光灯",
"status": "状态", "status": "状态",
"status_description": "检查网站状态", "status_description": "检查网站状态",
@@ -121,7 +121,7 @@
"generate_token": "生成令牌", "generate_token": "生成令牌",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "包含在 URL 内", "include_in_url": "包含在 URL 内",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "了解更多", "learn": "了解更多",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
@@ -162,15 +162,15 @@
"renamed": "集合已更名", "renamed": "集合已更名",
"request_in_use": "请求正在使用中", "request_in_use": "请求正在使用中",
"save_as": "另存为", "save_as": "另存为",
"save_to_collection": "Save to Collection", "save_to_collection": "保存至集合",
"select": "选择一个集合", "select": "选择一个集合",
"select_location": "选择位置", "select_location": "选择位置",
"select_team": "选择一个团队", "select_team": "选择一个团队",
"team_collections": "团队集合" "team_collections": "团队集合"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Are you sure you want to close this tab?", "close_unsaved_tab": "你确定要关闭此标签页吗?",
"close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.", "close_unsaved_tabs": "你确定要关闭所有标签页吗? {count} 个未保存的标签页将被丢失。",
"exit_team": "你确定要离开此团队吗?", "exit_team": "你确定要离开此团队吗?",
"logout": "你确定要登出吗?", "logout": "你确定要登出吗?",
"remove_collection": "你确定要永久删除该集合吗?", "remove_collection": "你确定要永久删除该集合吗?",
@@ -186,9 +186,9 @@
"sync": "您确定要同步该工作区吗?" "sync": "您确定要同步该工作区吗?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "Add to parameters", "add_parameters": "添加至参数",
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "在新标签页中打开请求",
"set_environment_variable": "Set as variable" "set_environment_variable": "设置为变量"
}, },
"cookies": { "cookies": {
"modal": { "modal": {
@@ -244,7 +244,7 @@
"team_name": "团队名称为空", "team_name": "团队名称为空",
"teams": "团队为空", "teams": "团队为空",
"tests": "没有针对该请求的测试", "tests": "没有针对该请求的测试",
"shortcodes": "Shortcodes 为空" "shortcodes": "短链接为空"
}, },
"environment": { "environment": {
"add_to_global": "添加到全局环境", "add_to_global": "添加到全局环境",
@@ -252,32 +252,32 @@
"create_new": "创建新环境", "create_new": "创建新环境",
"created": "环境已创建", "created": "环境已创建",
"deleted": "环境已删除", "deleted": "环境已删除",
"duplicated": "Environment duplicated", "duplicated": "环境已复制",
"edit": "编辑环境", "edit": "编辑环境",
"empty_variables": "No variables", "empty_variables": "没有变量",
"global": "Global", "global": "全局",
"global_variables": "Global variables", "global_variables": "全局变量",
"import_or_create": "Import or create a environment", "import_or_create": "Import or create a environment",
"invalid_name": "请提供有效的环境名称", "invalid_name": "请提供有效的环境名称",
"list": "Environment variables", "list": "环境变量",
"my_environments": "我的环境", "my_environments": "我的环境",
"name": "Name", "name": "名称",
"nested_overflow": "环境嵌套深度超过限制10层", "nested_overflow": "环境嵌套深度超过限制10层",
"new": "新建环境", "new": "新建环境",
"no_active_environment": "No active environment", "no_active_environment": "没有激活的环境",
"no_environment": "无环境", "no_environment": "无环境",
"no_environment_description": "没有选择环境。选择如何处理以下变量。", "no_environment_description": "没有选择环境。选择如何处理以下变量。",
"quick_peek": "Environment Quick Peek", "quick_peek": "快速浏览环境",
"replace_with_variable": "Replace with variable", "replace_with_variable": "替换为变量",
"scope": "Scope", "scope": "范围",
"select": "选择环境", "select": "选择环境",
"set": "Set environment", "set": "设置环境",
"set_as_environment": "Set as environment", "set_as_environment": "设置为环境",
"team_environments": "团队环境", "team_environments": "团队环境",
"title": "环境", "title": "环境",
"updated": "环境已更新", "updated": "环境已更新",
"value": "Value", "value": "",
"variable": "Variable", "variable": "变量",
"variable_list": "变量列表" "variable_list": "变量列表"
}, },
"error": { "error": {
@@ -296,6 +296,7 @@
"incorrect_email": "电子邮箱错误", "incorrect_email": "电子邮箱错误",
"invalid_link": "无效链接", "invalid_link": "无效链接",
"invalid_link_description": "你点击的链接无效或已过期。", "invalid_link_description": "你点击的链接无效或已过期。",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "不合法的 JSON", "json_parsing_failed": "不合法的 JSON",
"json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试", "json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试",
"network_error": "好像发生了网络错误,请重试。", "network_error": "好像发生了网络错误,请重试。",
@@ -306,7 +307,7 @@
"no_results_found": "找不到结果", "no_results_found": "找不到结果",
"page_not_found": "找不到此頁面", "page_not_found": "找不到此頁面",
"please_install_extension": "Please install the extension and add origin to the extension.", "please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "代理错误",
"script_fail": "无法执行预请求脚本", "script_fail": "无法执行预请求脚本",
"something_went_wrong": "发生了一些错误", "something_went_wrong": "发生了一些错误",
"test_script_fail": "无法执行请求脚本" "test_script_fail": "无法执行请求脚本"
@@ -314,10 +315,13 @@
"export": { "export": {
"as_json": "导出为 JSON", "as_json": "导出为 JSON",
"create_secret_gist": "创建私密 Gist", "create_secret_gist": "创建私密 Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist",
"failed": "Something went wrong while exporting", "failed": "Something went wrong while exporting",
"gist_created": "已创建 Gist", "secret_gist_success": "Successfully exported as secret Gist",
"require_github": "使用 GitHub 登录以创建私密 Gist", "require_github": "使用 GitHub 登录以创建私密 Gist",
"title": "导出" "title": "导出",
"success": "Successfully exported",
"gist_created": "已创建 Gist"
}, },
"filter": { "filter": {
"all": "全部", "all": "全部",
@@ -333,13 +337,13 @@
"renamed": "文件夹已更名" "renamed": "文件夹已更名"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?", "connection_switch_confirm": "您想连接最新的 GraphQL 端点吗?",
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is", "connection_switch_new_url": "切换到标签页将使您与活动的 GraphQL 连接断开。新的连接 URL ",
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is", "connection_switch_url": "您已连接到 GraphQL 端点,连接 URL ",
"mutations": "变更", "mutations": "变更",
"schema": "模式", "schema": "模式",
"subscriptions": "订阅", "subscriptions": "订阅",
"switch_connection": "Switch connection" "switch_connection": "切换连接"
}, },
"graphql_collections": { "graphql_collections": {
"title": "GraphQL Collections" "title": "GraphQL Collections"
@@ -408,27 +412,27 @@
"title": "导入" "title": "导入"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "查可能的错误",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "添加到环境",
"not_found": "Environment variable “{environment}” not found." "not_found": "环境变量“{environment}”未找到。"
}, },
"header": { "header": {
"cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead." "cookie": "浏览器不允许 Hoppscotch 设置 Cookie 标头。当前我们正在开发 Hoppscotch 桌面应用程序(即将推出),与此同时请改用授权标头。"
}, },
"response": { "response": {
"401_error": "Please check your authentication credentials.", "401_error": "请检查您的身份验证凭据。",
"404_error": "Please check your request URL and method type.", "404_error": "请检查您的请求 URL 和方法类型。",
"cors_error": "Please check your Cross-Origin Resource Sharing configuration.", "cors_error": "请检查您的跨源资源共享配置。",
"default_error": "Please check your request.", "default_error": "请检查您的请求。",
"network_error": "Please check your network connection." "network_error": "请检查您的网络连接。"
}, },
"title": "Inspector", "title": "Inspector",
"url": { "url": {
"extension_not_installed": "Extension not installed.", "extension_not_installed": "未安装扩展。",
"extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.", "extension_unknown_origin": "确保您已将 API 端点的源添加到 Hoppscotch 浏览器扩展列表中。",
"extention_enable_action": "Enable Browser Extension", "extention_enable_action": "启用浏览器扩展",
"extention_not_enabled": "Extension not enabled." "extention_not_enabled": "扩展未启用。"
} }
}, },
"layout": { "layout": {
@@ -460,10 +464,10 @@
"invalid_topic": "请提供该订阅的主题", "invalid_topic": "请提供该订阅的主题",
"keep_alive": "Keep Alive", "keep_alive": "Keep Alive",
"log": "日志", "log": "日志",
"lw_message": "Last-Will Message", "lw_message": "遗嘱消息",
"lw_qos": "Last-Will QoS", "lw_qos": "遗嘱消息QoS",
"lw_retain": "Last-Will Retain", "lw_retain": "遗嘱消息保留",
"lw_topic": "Last-Will Topic", "lw_topic": "遗嘱消息主题",
"message": "消息", "message": "消息",
"new": "新订阅", "new": "新订阅",
"not_connected": "请先启动MQTT连接。", "not_connected": "请先启动MQTT连接。",
@@ -544,7 +548,7 @@
"payload": "负载", "payload": "负载",
"query": "查询", "query": "查询",
"raw_body": "原始请求体", "raw_body": "原始请求体",
"rename": "Rename Request", "rename": "重命名请求",
"renamed": "请求重命名", "renamed": "请求重命名",
"run": "运行", "run": "运行",
"save": "保存", "save": "保存",
@@ -553,7 +557,7 @@
"share": "分享", "share": "分享",
"share_description": "分享 Hoppscotch 给你的朋友", "share_description": "分享 Hoppscotch 给你的朋友",
"share_request": "Share Request", "share_request": "Share Request",
"stop": "Stop", "stop": "停止",
"title": "请求", "title": "请求",
"type": "请求类型", "type": "请求类型",
"url": "URL", "url": "URL",
@@ -684,26 +688,26 @@
"title": "导航" "title": "导航"
}, },
"others": { "others": {
"prettify": "Prettify Editor's Content", "prettify": "美化内容",
"title": "Others" "title": "其他"
}, },
"request": { "request": {
"delete_method": "选择 DELETE 方法", "delete_method": "选择 DELETE 方法",
"get_method": "选择 GET 方法", "get_method": "选择 GET 方法",
"head_method": "选择 HEAD 方法", "head_method": "选择 HEAD 方法",
"import_curl": "Import cURL", "import_curl": "导入cURL",
"method": "方法", "method": "方法",
"next_method": "选择下一个方法", "next_method": "选择下一个方法",
"post_method": "选择 POST 方法", "post_method": "选择 POST 方法",
"previous_method": "选择上一个方法", "previous_method": "选择上一个方法",
"put_method": "选择 PUT 方法", "put_method": "选择 PUT 方法",
"rename": "Rename Request", "rename": "重命名请求",
"reset_request": "重置请求", "reset_request": "重置请求",
"save_request": "Save Request", "save_request": "保存请求",
"save_to_collections": "保存到集合", "save_to_collections": "保存到集合",
"send_request": "发送请求", "send_request": "发送请求",
"share_request": "Share Request", "share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "生成代码片段",
"title": "请求", "title": "请求",
"copy_request_link": "复制请求链接" "copy_request_link": "复制请求链接"
}, },
@@ -735,82 +739,82 @@
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "Change Language", "change_language": "更改语言",
"environments": { "environments": {
"delete": "Delete current environment", "delete": "删除当前环境",
"duplicate": "Duplicate current environment", "duplicate": "复制当前环境",
"duplicate_global": "Duplicate global environment", "duplicate_global": "复制全局环境",
"edit": "Edit current environment", "edit": "编辑当前环境",
"edit_global": "Edit global environment", "edit_global": "编辑全局环境",
"new": "Create new environment", "new": "创建新环境",
"new_variable": "Create a new environment variable", "new_variable": "创建新的环境变量",
"title": "Environments" "title": "环境"
}, },
"general": { "general": {
"chat": "Chat with support", "chat": "与支持人员聊天",
"help_menu": "Help and support", "help_menu": "帮助和支持",
"open_docs": "Read Documentation", "open_docs": "阅读文档",
"open_github": "Open GitHub repository", "open_github": "打开 GitHub 存储库",
"open_keybindings": "Keyboard shortcuts", "open_keybindings": "键盘快捷键",
"social": "Social", "social": "社交媒体",
"title": "General" "title": "一般"
}, },
"graphql": { "graphql": {
"connect": "Connect to server", "connect": "连接到服务器",
"disconnect": "Disconnect from server" "disconnect": "与服务器断开连接"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "Invite your friends to Hoppscotch", "invite": "邀请你的朋友来 Hoppscotch",
"title": "Miscellaneous" "title": "杂项"
}, },
"request": { "request": {
"save_as_new": "Save as new request", "save_as_new": "另存为新请求",
"select_method": "Select method", "select_method": "选择方法",
"switch_to": "Switch to", "switch_to": "切换到",
"tab_authorization": "Authorization tab", "tab_authorization": "授权标签页",
"tab_body": "Body tab", "tab_body": "请求体标签页",
"tab_headers": "Headers tab", "tab_headers": "请求头标签页",
"tab_parameters": "Parameters tab", "tab_parameters": "参数标签页",
"tab_pre_request_script": "Pre-request script tab", "tab_pre_request_script": "预请求脚本标签页",
"tab_query": "Query tab", "tab_query": "查询标签页",
"tab_tests": "Tests tab", "tab_tests": "测试标签页b",
"tab_variables": "Variables tab" "tab_variables": "变量标签页"
}, },
"response": { "response": {
"copy": "Copy response", "copy": "复制响应",
"download": "Download response as file", "download": "将响应下载为文件",
"title": "Response" "title": "响应"
}, },
"section": { "section": {
"interceptor": "Interceptor", "interceptor": "拦截器",
"interface": "Interface", "interface": "界面",
"theme": "Theme", "theme": "主题",
"user": "User" "user": "用户"
}, },
"settings": { "settings": {
"change_interceptor": "Change Interceptor", "change_interceptor": "更改拦截器",
"change_language": "Change Language", "change_language": "更改语言",
"theme": { "theme": {
"black": "Black", "black": "黑色",
"dark": "Dark", "dark": "暗色",
"light": "Light", "light": "亮色",
"system": "System preference" "system": "系统"
} }
}, },
"tab": { "tab": {
"close_current": "Close current tab", "close_current": "关闭当前标签页",
"close_others": "Close all other tabs", "close_others": "关闭所有其他标签页",
"duplicate": "Duplicate current tab", "duplicate": "复制当前标签页",
"new_tab": "Open a new tab", "new_tab": "打开新的标签页",
"title": "Tabs" "title": "标签页"
}, },
"workspace": { "workspace": {
"delete": "Delete current team", "delete": "删除当前团队",
"edit": "Edit current team", "edit": "编辑当前团队",
"invite": "Invite people to team", "invite": "邀请人员加入团队",
"new": "Create new team", "new": "创建新团队",
"switch_to_personal": "Switch to your personal workspace", "switch_to_personal": "切换到您的个人工作空间",
"title": "Teams" "title": "团队"
} }
}, },
"sse": { "sse": {
@@ -983,10 +987,10 @@
"shortcodes": { "shortcodes": {
"actions": "操作", "actions": "操作",
"created_on": "创建于", "created_on": "创建于",
"deleted": "已刪除快捷键", "deleted": "已刪除短链接",
"method": "方法", "method": "方法",
"not_found": "找不到快捷键", "not_found": "找不到短链接",
"short_code": "快捷键", "short_code": "短链接",
"url": "URL" "url": "URL"
} }
} }

View File

@@ -24,6 +24,7 @@
"go_back": "Go back", "go_back": "Go back",
"go_forward": "Go forward", "go_forward": "Go forward",
"group_by": "Group by", "group_by": "Group by",
"hide_secret": "Hide secret",
"label": "Label", "label": "Label",
"learn_more": "Learn more", "learn_more": "Learn more",
"less": "Less", "less": "Less",
@@ -43,6 +44,7 @@
"search": "Search", "search": "Search",
"send": "Send", "send": "Send",
"share": "Share", "share": "Share",
"show_secret": "Show secret",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -237,7 +239,9 @@
"pending_invites": "There are no pending invites for this team", "pending_invites": "There are no pending invites for this team",
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protocols are empty", "protocols": "Protocols are empty",
"request_variables": "This request does not have any request variables",
"schema": "Connect to a GraphQL endpoint to view schema", "schema": "Connect to a GraphQL endpoint to view schema",
"secret_environments": "Secrets are not synced to Hoppscotch",
"shared_requests": "Shared requests are empty", "shared_requests": "Shared requests are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one", "shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
@@ -269,6 +273,8 @@
"quick_peek": "Environment Quick Peek", "quick_peek": "Environment Quick Peek",
"replace_with_variable": "Replace with variable", "replace_with_variable": "Replace with variable",
"scope": "Scope", "scope": "Scope",
"secrets": "Secrets",
"secret_value": "Secret value",
"select": "Select environment", "select": "Select environment",
"set": "Set environment", "set": "Set environment",
"set_as_environment": "Set as environment", "set_as_environment": "Set as environment",
@@ -277,6 +283,7 @@
"updated": "Environment updated", "updated": "Environment updated",
"value": "Value", "value": "Value",
"variable": "Variable", "variable": "Variable",
"variables":"Variables",
"variable_list": "Variable List" "variable_list": "Variable List"
}, },
"error": { "error": {
@@ -309,7 +316,8 @@
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Could not execute pre-request script", "script_fail": "Could not execute pre-request script",
"something_went_wrong": "Something went wrong", "something_went_wrong": "Something went wrong",
"test_script_fail": "Could not execute post-request script" "test_script_fail": "Could not execute post-request script",
"reading_files": "Error while reading one or more files."
}, },
"export": { "export": {
"as_json": "Export as JSON", "as_json": "Export as JSON",
@@ -407,12 +415,17 @@
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"postman_environment": "Postman Environment", "postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Import" "title": "Import",
"file_size_limit_exceeded_warning_multiple_files": "Chosen files exceed the recommended limit of 10MB. Only the first {files} selected will be imported",
"file_size_limit_exceeded_warning_single_file": "The currently chosen file exceeds the recommended limit of 10MB. Please select another file.",
"success": "Successfully imported"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Inspect possible errors",
"environment": { "environment": {
"add_environment": "Add to Environment", "add_environment": "Add to Environment",
"add_environment_value": "Add value",
"empty_value": "Environment value is empty for the variable '{variable}' ",
"not_found": "Environment variable “{environment}” not found." "not_found": "Environment variable “{environment}” not found."
}, },
"header": { "header": {
@@ -548,6 +561,7 @@
"raw_body": "Raw Request Body", "raw_body": "Raw Request Body",
"rename": "Rename Request", "rename": "Rename Request",
"renamed": "Request renamed", "renamed": "Request renamed",
"request_variables": "Request variables",
"run": "Run", "run": "Run",
"save": "Save", "save": "Save",
"save_as": "Save as", "save_as": "Save as",
@@ -889,6 +903,7 @@
"query": "Query", "query": "Query",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests", "shared_requests": "Shared Requests",
"share_tab_request": "Share tab request",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Tests", "tests": "Tests",

View File

@@ -1,6 +1,6 @@
{ {
"action": { "action": {
"add": "Add", "add": "Добавить",
"autoscroll": "Автоскрол", "autoscroll": "Автоскрол",
"cancel": "Отменить", "cancel": "Отменить",
"choose_file": "Выберите файл", "choose_file": "Выберите файл",
@@ -121,7 +121,7 @@
"generate_token": "Сгенерировать токен", "generate_token": "Сгенерировать токен",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Добавить в URL", "include_in_url": "Добавить в URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ", "inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "Узнать больше", "learn": "Узнать больше",
"oauth": { "oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
@@ -148,7 +148,7 @@
"created": "Коллекция создана", "created": "Коллекция создана",
"different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией", "different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией",
"edit": "Редактировать коллекцию", "edit": "Редактировать коллекцию",
"import_or_create": "Import or create a collection", "import_or_create": "Вы можете импортировать существующую или создать новую коллекцию",
"invalid_name": "Укажите допустимое название коллекции", "invalid_name": "Укажите допустимое название коллекции",
"invalid_root_move": "Коллекция уже в корне", "invalid_root_move": "Коллекция уже в корне",
"moved": "Перемещено успешно", "moved": "Перемещено успешно",
@@ -170,7 +170,7 @@
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "Вы уверены что хотите закрыть эту вкладку?", "close_unsaved_tab": "Вы уверены что хотите закрыть эту вкладку?",
"close_unsaved_tabs": "ВЫ уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.", "close_unsaved_tabs": "Вы уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.",
"exit_team": "Вы точно хотите покинуть эту команду?", "exit_team": "Вы точно хотите покинуть эту команду?",
"logout": "Вы действительно хотите выйти?", "logout": "Вы действительно хотите выйти?",
"remove_collection": "Вы уверены, что хотите навсегда удалить эту коллекцию?", "remove_collection": "Вы уверены, что хотите навсегда удалить эту коллекцию?",
@@ -192,20 +192,20 @@
}, },
"cookies": { "cookies": {
"modal": { "modal": {
"cookie_expires": "Expires", "cookie_expires": "Истекает",
"cookie_name": "Name", "cookie_name": "Имя",
"cookie_path": "Path", "cookie_path": "Путь",
"cookie_string": "Cookie string", "cookie_string": "Cookie параметры",
"cookie_value": "Value", "cookie_value": "Значение",
"empty_domain": "Domain is empty", "empty_domain": "Нужно заполнить домен",
"empty_domains": "Domain list is empty", "empty_domains": "Список доменов пуст",
"enter_cookie_string": "Enter cookie string", "enter_cookie_string": "Введите cookie параметры сюда",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.", "interceptor_no_support": "Ваш текущий Перехватчик не поддерживает работу с cookie. Выберите другой Перехватчик и попробуйте еще раз.",
"managed_tab": "Managed", "managed_tab": "Managed",
"new_domain_name": "New domain name", "new_domain_name": "Добавить новый домен",
"no_cookies_in_domain": "No cookies set for this domain", "no_cookies_in_domain": "Никаких cookie не установлено для этого домена",
"raw_tab": "Raw", "raw_tab": "Raw",
"set": "Set a cookie" "set": "Установить cookie"
} }
}, },
"count": { "count": {
@@ -257,14 +257,14 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment", "import_or_create": "Импортировать или создать новое окружение",
"invalid_name": "Укажите допустимое имя для окружения", "invalid_name": "Укажите допустимое имя для окружения",
"list": "Environment variables", "list": "Переменные окружения",
"my_environments": "Мои окружения", "my_environments": "Мои окружения",
"name": "Name", "name": "Name",
"nested_overflow": "максимальный уровень вложения переменных окружения - 10", "nested_overflow": "максимальный уровень вложения переменных окружения - 10",
"new": "Новая среда", "new": "Новая среда",
"no_active_environment": "No active environment", "no_active_environment": "Нет активных окружений",
"no_environment": "Нет окружения", "no_environment": "Нет окружения",
"no_environment_description": "Не выбрано окружение, выберите что делать с переменными.", "no_environment_description": "Не выбрано окружение, выберите что делать с переменными.",
"quick_peek": "Environment Quick Peek", "quick_peek": "Environment Quick Peek",
@@ -284,7 +284,7 @@
"authproviders_load_error": "Unable to load auth providers", "authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.", "browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
"check_console_details": "Подробности смотрите в журнале консоли.", "check_console_details": "Подробности смотрите в журнале консоли.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Инструкция как добавить origin в настройки расширения",
"curl_invalid_format": "cURL неправильно отформатирован", "curl_invalid_format": "cURL неправильно отформатирован",
"danger_zone": "Опасная зона", "danger_zone": "Опасная зона",
"delete_account": "Вы являетесь владельцем этой команды:", "delete_account": "Вы являетесь владельцем этой команды:",
@@ -296,16 +296,17 @@
"incorrect_email": "Не корректный Email", "incorrect_email": "Не корректный Email",
"invalid_link": "Не корректная ссылка", "invalid_link": "Не корректная ссылка",
"invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.", "invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "Не корректный JSON", "json_parsing_failed": "Не корректный JSON",
"json_prettify_invalid_body": "Не удалось определить недопустимое тело, устранить синтаксические ошибки json и повторить попытку.", "json_prettify_invalid_body": "Не удалось определить недопустимое тело, устранить синтаксические ошибки json и повторить попытку.",
"network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.", "network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.",
"network_fail": "Не удалось отправить запрос", "network_fail": "Не удалось отправить запрос",
"no_collections_to_export": "No collections to export. Please create a collection to get started.", "no_collections_to_export": "Нечего экспортировать. Для начала нужно создать коллекцию.",
"no_duration": "Без продолжительности", "no_duration": "Без продолжительности",
"no_environments_to_export": "No environments to export. Please create an environment to get started.", "no_environments_to_export": "Нечего экспортировать. Для начала нужно создать переменные окружения.",
"no_results_found": "Совпадения не найдены", "no_results_found": "Совпадения не найдены",
"page_not_found": "Эта страница не найдена", "page_not_found": "Эта страница не найдена",
"please_install_extension": "Please install the extension and add origin to the extension.", "please_install_extension": "Нужно установить специальное расширение и добавить этот домен как новый origin в настройках расширения.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Не удалось выполнить сценарий предварительного запроса", "script_fail": "Не удалось выполнить сценарий предварительного запроса",
"something_went_wrong": "Что-то пошло не так", "something_went_wrong": "Что-то пошло не так",
@@ -314,10 +315,13 @@
"export": { "export": {
"as_json": "Экспорт как JSON", "as_json": "Экспорт как JSON",
"create_secret_gist": "Создать секретный Gist", "create_secret_gist": "Создать секретный Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist",
"failed": "Something went wrong while exporting", "failed": "Something went wrong while exporting",
"gist_created": "Gist создан", "secret_gist_success": "Successfully exported as secret Gist",
"require_github": "Войдите через GitHub, чтобы создать секретную суть", "require_github": "Войдите через GitHub, чтобы создать секретную суть",
"title": "Экспорт" "title": "Экспорт",
"success": "Successfully exported",
"gist_created": "Gist создан"
}, },
"filter": { "filter": {
"all": "Все", "all": "Все",
@@ -375,7 +379,7 @@
}, },
"import": { "import": {
"collections": "Импортировать коллекции", "collections": "Импортировать коллекции",
"curl": "Импортировать cURL", "curl": "Импортировать из cURL",
"environments_from_gist": "Import From Gist", "environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist", "environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Ошибка импорта", "failed": "Ошибка импорта",
@@ -523,10 +527,10 @@
"different_collection": "Нельзя изменять порядок запросов из разных коллекций", "different_collection": "Нельзя изменять порядок запросов из разных коллекций",
"duplicated": "Запрос скопирован", "duplicated": "Запрос скопирован",
"duration": "Продолжительность", "duration": "Продолжительность",
"enter_curl": "Введите cURL", "enter_curl": "Введите сюда команду cURL",
"generate_code": "Сгенерировать код", "generate_code": "Сгенерировать код",
"generated_code": "Сгенерированный код", "generated_code": "Сгенерированный код",
"go_to_authorization_tab": "Go to Authorization tab", "go_to_authorization_tab": "Go to Authorization",
"go_to_body_tab": "Go to Body tab", "go_to_body_tab": "Go to Body tab",
"header_list": "Список заголовков", "header_list": "Список заголовков",
"invalid_name": "Укажите имя для запроса", "invalid_name": "Укажите имя для запроса",
@@ -544,7 +548,7 @@
"payload": "Полезная нагрузка", "payload": "Полезная нагрузка",
"query": "Запрос", "query": "Запрос",
"raw_body": "Необработанное тело запроса", "raw_body": "Необработанное тело запроса",
"rename": "Rename Request", "rename": "Переименость запрос",
"renamed": "Запрос переименован", "renamed": "Запрос переименован",
"run": "Запустить", "run": "Запустить",
"save": "Сохранить", "save": "Сохранить",
@@ -691,13 +695,13 @@
"delete_method": "Выберите метод DELETE", "delete_method": "Выберите метод DELETE",
"get_method": "Выберите метод GET", "get_method": "Выберите метод GET",
"head_method": "Выберите метод HEAD", "head_method": "Выберите метод HEAD",
"import_curl": "Import cURL", "import_curl": "Импортировать из cURL",
"method": "Методика", "method": "Методика",
"next_method": "Выберите следующий метод", "next_method": "Выберите следующий метод",
"post_method": "Выберите метод POST", "post_method": "Выберите метод POST",
"previous_method": "Выбрать предыдущий метод", "previous_method": "Выбрать предыдущий метод",
"put_method": "Выберите метод PUT", "put_method": "Выберите метод PUT",
"rename": "Rename Request", "rename": "Переименовать запрос",
"reset_request": "Сбросить запрос", "reset_request": "Сбросить запрос",
"save_request": "Сохарнить запрос", "save_request": "Сохарнить запрос",
"save_to_collections": "Сохранить в коллекции", "save_to_collections": "Сохранить в коллекции",
@@ -874,8 +878,8 @@
"tab": { "tab": {
"authorization": "Авторизация", "authorization": "Авторизация",
"body": "Тело", "body": "Тело",
"close": "Close Tab", "close": "Закрыть вкладку",
"close_others": "Close other Tabs", "close_others": "Закрыть остальные вкладки",
"collections": "Коллекции", "collections": "Коллекции",
"documentation": "Документация", "documentation": "Документация",
"duplicate": "Duplicate Tab", "duplicate": "Duplicate Tab",
@@ -905,7 +909,7 @@
"email_do_not_match": "Электронная почта, которой Вы воспользовались не соответсвует указанной в данных Вашей учетной записи.", "email_do_not_match": "Электронная почта, которой Вы воспользовались не соответсвует указанной в данных Вашей учетной записи.",
"exit": "Выйти из команды", "exit": "Выйти из команды",
"exit_disabled": "Только владелец не может выйти из команды", "exit_disabled": "Только владелец не может выйти из команды",
"failed_invites": "Failed invites", "failed_invites": "Непринятые приглашения",
"invalid_coll_id": "Не верный идентификатор коллекции", "invalid_coll_id": "Не верный идентификатор коллекции",
"invalid_email_format": "Формат электронной почты недействителен", "invalid_email_format": "Формат электронной почты недействителен",
"invalid_id": "Некорректный ID команды. Свяжитесь с руководителем команды.", "invalid_id": "Некорректный ID команды. Свяжитесь с руководителем команды.",
@@ -947,7 +951,7 @@
"same_target_destination": "Таже цель и конечная точка", "same_target_destination": "Таже цель и конечная точка",
"saved": "Команда сохранена", "saved": "Команда сохранена",
"select_a_team": "Выбрать команду", "select_a_team": "Выбрать команду",
"success_invites": "Success invites", "success_invites": "Принятые приглашения",
"title": "Команды", "title": "Команды",
"we_sent_invite_link": "Мы отправили все приглашения!", "we_sent_invite_link": "Мы отправили все приглашения!",
"we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду." "we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду."

View File

@@ -1,7 +1,7 @@
{ {
"name": "@hoppscotch/common", "name": "@hoppscotch/common",
"private": true, "private": true,
"version": "2023.12.1-1", "version": "2023.12.6",
"scripts": { "scripts": {
"dev": "pnpm exec npm-run-all -p -l dev:*", "dev": "pnpm exec npm-run-all -p -l dev:*",
"test": "vitest --run", "test": "vitest --run",

View File

@@ -1,12 +1,12 @@
<svg width="119" height="117" viewBox="0 0 119 117" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="119" height="117" viewBox="0 0 119 117" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M52.0114 115.804C80.1842 115.804 103.023 92.8871 103.023 64.6183C103.023 36.3495 80.1842 13.4331 52.0114 13.4331C23.8386 13.4331 1 36.3495 1 64.6183C1 92.8871 23.8386 115.804 52.0114 115.804Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/> <path d="M52.0114 115.804C80.1842 115.804 103.023 92.8871 103.023 64.6183C103.023 36.3495 80.1842 13.4331 52.0114 13.4331C23.8386 13.4331 1 36.3495 1 64.6183C1 92.8871 23.8386 115.804 52.0114 115.804Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M110.666 21.9438C111.335 21.3298 111.382 20.2875 110.77 19.6157C110.158 18.9439 109.12 18.897 108.45 19.5109C107.781 20.1249 107.734 21.1672 108.346 21.839C108.957 22.5108 109.996 22.5577 110.666 21.9438Z" fill="#525252"/> <path d="M110.666 21.9438C111.335 21.3298 111.382 20.2875 110.77 19.6157C110.158 18.9439 109.12 18.897 108.45 19.5109C107.781 20.1249 107.734 21.1672 108.346 21.839C108.957 22.5108 109.996 22.5577 110.666 21.9438Z" fill="#575757"/>
<path opacity="0.3" d="M83.4192 41.2397C85.2331 41.2397 86.7036 39.7642 86.7036 37.9441C86.7036 36.1239 85.2331 34.6484 83.4192 34.6484C81.6053 34.6484 80.1348 36.1239 80.1348 37.9441C80.1348 39.7642 81.6053 41.2397 83.4192 41.2397Z" fill="#525252"/> <path opacity="0.3" d="M83.419 41.2397C85.2329 41.2397 86.7034 39.7642 86.7034 37.9441C86.7034 36.1239 85.2329 34.6484 83.419 34.6484C81.605 34.6484 80.1345 36.1239 80.1345 37.9441C80.1345 39.7642 81.605 41.2397 83.419 41.2397Z" fill="#575757"/>
<path d="M61.2816 45.569C60.8718 47.7548 59.0958 49.804 56.7733 50.3505C54.9973 50.7604 53.2213 50.3505 51.9918 49.3942V65.2415H37.2374C37.7839 64.1486 37.9205 63.0557 37.7839 61.8261C37.5106 59.0939 35.1882 56.908 32.5925 56.6348C29.0405 56.2249 25.8984 58.9572 25.8984 62.5092C25.8984 63.4655 26.035 64.4218 26.4448 65.1049H9.77783V34.6398C9.77783 28.3555 14.8326 23.3008 21.2535 23.3008H51.8552V39.1481C52.9481 38.465 54.041 38.0552 55.2706 38.0552C58.9592 38.6016 61.9647 41.7438 61.2816 45.569Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M61.2815 45.569C60.8716 47.7548 59.0956 49.804 56.7732 50.3505C54.9972 50.7603 53.2212 50.3505 51.9917 49.3942V65.2415H37.2373C37.7837 64.1486 37.9204 63.0557 37.7837 61.8261C37.5105 59.0939 35.1881 56.908 32.5924 56.6348C29.0404 56.2249 25.8983 58.9572 25.8983 62.5092C25.8983 63.4655 26.0349 64.4218 26.4447 65.1049H9.77771V34.6398C9.77771 28.3555 14.8325 23.3008 21.2534 23.3008H51.8551V39.1481C52.948 38.465 54.0409 38.0552 55.2704 38.0552C58.959 38.6016 61.9646 41.7438 61.2815 45.569Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M42.019 85.187C41.4726 88.8756 44.3415 92.0177 48.0301 92.0177C49.5328 92.0177 50.899 91.4713 51.9919 90.6516V107.045H21.3902C15.1059 107.045 9.91455 101.854 9.91455 95.7063V65.1046H26.4449C26.0351 64.2849 25.8985 63.4653 25.8985 62.3723C26.0351 59.5034 28.2209 57.0444 30.9532 56.6345C34.6418 56.2247 37.784 58.957 37.784 62.5089C37.784 63.4652 37.6474 64.2849 37.2375 65.1046H51.7187V81.4984C50.4891 80.4055 48.8498 79.859 47.0738 80.1323C44.4781 80.4055 42.4289 82.5913 42.019 85.187Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M42.0188 85.187C41.4723 88.8756 44.3412 92.0177 48.0298 92.0177C49.5326 92.0177 50.8987 91.4713 51.9917 90.6516V107.045H21.3899C15.1057 107.045 9.91431 101.854 9.91431 95.7063V65.1046H26.4447C26.0348 64.2849 25.8982 63.4653 25.8982 62.3723C26.0348 59.5034 28.2207 57.0444 30.953 56.6345C34.6416 56.2247 37.7837 58.957 37.7837 62.5089C37.7837 63.4652 37.6471 64.2849 37.2373 65.1046H51.7184V81.4984C50.4889 80.4055 48.8495 79.859 47.0735 80.1323C44.4778 80.4055 42.4286 82.5913 42.0188 85.187Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M94.0691 65.1047V95.4331C94.0691 101.717 88.8777 106.772 82.5934 106.772H51.9917V90.3784C50.8988 91.3347 49.5327 91.7446 48.0299 91.7446C44.4779 91.7446 41.7456 88.6024 42.0188 84.9138C42.4287 82.1815 44.6145 80.1323 47.0736 79.7224C48.8496 79.5858 50.6256 80.1323 51.7185 81.0886V64.6948H69.3418C68.7953 65.5145 68.6587 66.4708 68.6587 67.4271C68.6587 70.8425 71.6642 73.7114 75.3528 73.3016C78.0851 73.0283 80.4076 70.7059 80.6808 68.1102C80.8174 66.8807 80.5442 65.7877 80.1344 64.6948L94.0691 65.1047Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M94.069 65.1047V95.4331C94.069 101.717 88.8776 106.772 82.5933 106.772H51.9916V90.3784C50.8987 91.3347 49.5325 91.7446 48.0298 91.7446C44.4778 91.7446 41.7455 88.6024 42.0187 84.9138C42.4286 82.1815 44.6144 80.1323 47.0735 79.7224C48.8495 79.5858 50.6254 80.1323 51.7184 81.0886V64.6948H69.3417C68.7952 65.5145 68.6586 66.4708 68.6586 67.4271C68.6586 70.8425 71.6641 73.7114 75.3527 73.3016C78.085 73.0283 80.4075 70.7059 80.6807 68.1102C80.8173 66.8807 80.5441 65.7877 80.1342 64.6948L94.069 65.1047Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M106.911 52.6728L93.3859 50.3503C93.6591 51.4432 93.7958 52.5362 93.3859 53.6291C92.5662 56.2247 90.1072 58.0007 87.3749 57.8641C83.8229 57.5909 81.2272 54.4488 81.7737 51.0334C81.9103 50.0771 82.3201 49.2574 82.8666 48.5743L65.3799 45.4322L68.1122 29.7215C69.2051 30.8144 70.8445 31.4975 72.7571 31.4975C75.2161 31.3609 77.2654 29.8581 77.9484 27.6723C79.3146 24.1203 76.9921 20.2951 73.3035 19.7486C72.074 19.4754 70.8445 19.7486 69.7516 20.1585L72.4839 4.44775L102.539 9.63912C108.823 10.732 112.922 16.6065 111.829 22.7541L106.911 52.6728Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M106.911 52.6728L93.3859 50.3503C93.6591 51.4432 93.7958 52.5362 93.3859 53.6291C92.5662 56.2248 90.1072 58.0007 87.3749 57.8641C83.8229 57.5909 81.2272 54.4488 81.7737 51.0334C81.9103 50.0771 82.3201 49.2574 82.8666 48.5743L65.3799 45.4322L68.1122 29.7215C69.2051 30.8144 70.8445 31.4975 72.7571 31.4975C75.2161 31.3609 77.2654 29.8581 77.9484 27.6723C79.3146 24.1203 76.9921 20.2951 73.3035 19.7486C72.074 19.4754 70.8445 19.7486 69.7516 20.1585L72.4839 4.44775L102.539 9.63912C108.823 10.732 112.922 16.6065 111.829 22.7541L106.911 52.6728Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M113.473 64.1927L118.015 62.8237C118.344 62.7282 118.344 62.2506 118.015 62.1551L113.473 60.7861C113.356 60.7543 113.271 60.6694 113.24 60.5526L111.871 56.0212C111.775 55.6922 111.297 55.6922 111.202 56.0212L109.833 60.5526C109.801 60.6694 109.716 60.7543 109.6 60.7861L105.068 62.1551C104.739 62.2506 104.739 62.7282 105.068 62.8237L109.61 64.1927C109.727 64.2245 109.812 64.3094 109.844 64.4261L111.213 68.9682C111.308 69.2972 111.786 69.2972 111.881 68.9682L113.25 64.4261C113.271 64.3094 113.356 64.2245 113.473 64.1927Z" fill="#525252"/> <path d="M113.473 64.1927L118.015 62.8237C118.344 62.7282 118.344 62.2506 118.015 62.1551L113.473 60.7861C113.356 60.7543 113.271 60.6694 113.239 60.5526L111.87 56.0212C111.775 55.6922 111.297 55.6922 111.202 56.0212L109.833 60.5526C109.801 60.6694 109.716 60.7543 109.599 60.7861L105.068 62.1551C104.739 62.2506 104.739 62.7282 105.068 62.8237L109.61 64.1927C109.727 64.2245 109.812 64.3094 109.843 64.4261L111.212 68.9682C111.308 69.2972 111.786 69.2972 111.881 68.9682L113.25 64.4261C113.271 64.3094 113.356 64.2245 113.473 64.1927Z" fill="#575757"/>
<path d="M103.424 24.4523L109.073 22.7496C109.482 22.6308 109.482 22.0369 109.073 21.9181L103.424 20.2153C103.279 20.1757 103.173 20.0701 103.133 19.925L101.431 14.2888C101.312 13.8796 100.718 13.8796 100.599 14.2888L98.8965 19.925C98.8569 20.0701 98.7513 20.1757 98.6061 20.2153L92.97 21.9181C92.5608 22.0369 92.5608 22.6308 92.97 22.7496L98.6193 24.4523C98.7645 24.4919 98.8701 24.5975 98.9097 24.7427L100.612 30.3921C100.731 30.8012 101.325 30.8012 101.444 30.3921L103.147 24.7427C103.173 24.5975 103.279 24.4919 103.424 24.4523Z" fill="#525252"/> <path d="M103.424 24.4523L109.073 22.7496C109.483 22.6308 109.483 22.0369 109.073 21.9181L103.424 20.2153C103.279 20.1757 103.173 20.0701 103.134 19.925L101.431 14.2888C101.312 13.8796 100.718 13.8796 100.599 14.2888L98.8967 19.925C98.8571 20.0701 98.7515 20.1757 98.6064 20.2153L92.9702 21.9181C92.561 22.0369 92.561 22.6308 92.9702 22.7496L98.6196 24.4523C98.7647 24.4919 98.8703 24.5975 98.9099 24.7427L100.613 30.3921C100.731 30.8012 101.325 30.8012 101.444 30.3921L103.147 24.7427C103.173 24.5975 103.279 24.4919 103.424 24.4523Z" fill="#575757"/>
<path d="M62.357 5.26099L65.1958 4.40543C65.4015 4.34575 65.4015 4.0473 65.1958 3.98761L62.357 3.13205C62.2841 3.11216 62.231 3.0591 62.2111 2.98615L61.3555 0.154199C61.2958 -0.0513995 60.9973 -0.0513995 60.9376 0.154199L60.082 2.98615C60.0621 3.0591 60.009 3.11216 59.9361 3.13205L57.1039 3.98761C56.8983 4.0473 56.8983 4.34575 57.1039 4.40543L59.9427 5.26099C60.0157 5.28088 60.0687 5.33394 60.0886 5.4069L60.9443 8.24548C61.004 8.45107 61.3024 8.45107 61.3621 8.24548L62.2177 5.4069C62.231 5.33394 62.2841 5.28088 62.357 5.26099Z" fill="#525252"/> <path d="M62.357 5.26099L65.1958 4.40544C65.4015 4.34575 65.4015 4.0473 65.1958 3.98761L62.357 3.13205C62.2841 3.11216 62.231 3.0591 62.2111 2.98615L61.3555 0.154199C61.2958 -0.0513995 60.9973 -0.0513995 60.9376 0.154199L60.082 2.98615C60.0621 3.0591 60.009 3.11216 59.9361 3.13205L57.1039 3.98761C56.8983 4.0473 56.8983 4.34575 57.1039 4.40544L59.9427 5.26099C60.0157 5.28088 60.0687 5.33394 60.0886 5.4069L60.9443 8.24547C61.004 8.45107 61.3024 8.45107 61.3621 8.24547L62.2177 5.4069C62.231 5.33394 62.2841 5.28088 62.357 5.26099Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -1,19 +1,19 @@
<svg width="150" height="126" viewBox="0 0 150 126" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="150" height="126" viewBox="0 0 150 126" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M84.2755 124.383C117.256 124.383 143.993 97.6454 143.993 64.5473C143.993 31.4493 117.138 4.71167 84.2755 4.71167C51.2952 4.71167 24.5576 31.4493 24.5576 64.5473C24.5576 97.6454 51.2952 124.383 84.2755 124.383Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M84.2752 124.383C117.256 124.383 143.993 97.6454 143.993 64.5473C143.993 31.4493 117.138 4.71167 84.2752 4.71167C51.295 4.71167 24.5574 31.4493 24.5574 64.5473C24.5574 97.6454 51.295 124.383 84.2752 124.383Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M139.635 25.4417C142.302 25.4417 144.464 23.2796 144.464 20.6125C144.464 17.9453 142.302 15.7832 139.635 15.7832C136.968 15.7832 134.806 17.9453 134.806 20.6125C134.806 23.2796 136.968 25.4417 139.635 25.4417Z" fill="#181818"/> <path d="M139.635 25.4417C142.302 25.4417 144.464 23.2796 144.464 20.6125C144.464 17.9453 142.302 15.7832 139.635 15.7832C136.968 15.7832 134.806 17.9453 134.806 20.6125C134.806 23.2796 136.968 25.4417 139.635 25.4417Z" fill="#0E0E0E"/>
<path d="M146.702 6.59605C148.523 6.59605 150 5.11948 150 3.29803C150 1.47658 148.523 0 146.702 0C144.88 0 143.404 1.47658 143.404 3.29803C143.404 5.11948 144.88 6.59605 146.702 6.59605Z" fill="#181818"/> <path d="M146.702 6.59605C148.523 6.59605 150 5.11948 150 3.29803C150 1.47658 148.523 0 146.702 0C144.881 0 143.404 1.47658 143.404 3.29803C143.404 5.11948 144.881 6.59605 146.702 6.59605Z" fill="#0E0E0E"/>
<path d="M27.2663 25.3241C29.0877 25.3241 30.5643 23.8475 30.5643 22.0261C30.5643 20.2046 29.0877 18.728 27.2663 18.728C25.4448 18.728 23.9683 20.2046 23.9683 22.0261C23.9683 23.8475 25.4448 25.3241 27.2663 25.3241Z" fill="#181818"/> <path d="M27.2665 25.3241C29.088 25.3241 30.5646 23.8475 30.5646 22.0261C30.5646 20.2046 29.088 18.728 27.2665 18.728C25.4451 18.728 23.9685 20.2046 23.9685 22.0261C23.9685 23.8475 25.4451 25.3241 27.2665 25.3241Z" fill="#0E0E0E"/>
<path d="M7.12491 88.9293C10.5076 88.9293 13.2498 86.187 13.2498 82.8044C13.2498 79.4217 10.5076 76.6794 7.12491 76.6794C3.74221 76.6794 1 79.4217 1 82.8044C1 86.187 3.74221 88.9293 7.12491 88.9293Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M7.12491 88.9293C10.5076 88.9293 13.2498 86.187 13.2498 82.8044C13.2498 79.4217 10.5076 76.6794 7.12491 76.6794C3.74221 76.6794 1 79.4217 1 82.8044C1 86.187 3.74221 88.9293 7.12491 88.9293Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M128.187 105.12C117.275 116.97 101.64 124.383 84.275 124.383C70.4162 124.383 57.6597 119.662 47.5264 111.731V14.0746C47.5264 10.8443 50.1275 8.20825 53.3724 8.20825H111.37L128.187 25.0838V105.12Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M128.187 105.12C117.275 116.97 101.64 124.383 84.275 124.383C70.4162 124.383 57.6597 119.662 47.5264 111.731V14.0746C47.5264 10.8443 50.1275 8.20825 53.3724 8.20825H111.37L128.187 25.0838V105.12Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M71.5926 104.186H63.1468C62.8317 104.186 62.5796 103.369 62.5796 102.39C62.5796 101.41 62.8317 100.593 63.1468 100.593H71.5926C71.9078 100.593 72.1599 101.41 72.1599 102.39C72.1599 103.533 71.9078 104.186 71.5926 104.186Z" fill="#525252"/> <path d="M71.5926 104.186H63.1468C62.8317 104.186 62.5796 103.369 62.5796 102.39C62.5796 101.41 62.8317 100.593 63.1468 100.593H71.5926C71.9078 100.593 72.1599 101.41 72.1599 102.39C72.1599 103.533 71.9078 104.186 71.5926 104.186Z" fill="#575757"/>
<path d="M90.1779 28.7408H63.7222C63.1069 28.7408 62.5796 27.9117 62.5796 26.9445C62.5796 25.9773 63.1069 25.1482 63.7222 25.1482H90.1779C90.7932 25.1482 91.3205 25.9773 91.3205 26.9445C91.3205 27.9117 90.7932 28.7408 90.1779 28.7408Z" fill="#525252"/> <path d="M90.1779 28.7408H63.7222C63.1069 28.7408 62.5796 27.9117 62.5796 26.9445C62.5796 25.9773 63.1069 25.1482 63.7222 25.1482H90.1779C90.7932 25.1482 91.3205 25.9773 91.3205 26.9445C91.3205 27.9117 90.7932 28.7408 90.1779 28.7408Z" fill="#575757"/>
<path d="M70.2489 37.1239H63.3525C62.9363 37.1239 62.5796 36.2948 62.5796 35.3276C62.5796 34.3603 62.9363 33.5312 63.3525 33.5312H70.1895C70.6056 33.5312 70.9624 34.3603 70.9624 35.3276C70.9624 36.2948 70.6056 37.1239 70.2489 37.1239Z" fill="#525252"/> <path d="M70.2489 37.1239H63.3525C62.9363 37.1239 62.5796 36.2948 62.5796 35.3276C62.5796 34.3603 62.9363 33.5312 63.3525 33.5312H70.1895C70.6056 33.5312 70.9624 34.3603 70.9624 35.3276C70.9624 36.2948 70.6056 37.1239 70.2489 37.1239Z" fill="#575757"/>
<path d="M111.994 83.8276H79.203H75.5888H64.6583C64.2176 83.8276 63.7769 84.5761 63.7769 85.6239C63.7769 86.5221 64.1295 87.4203 64.6583 87.4203H75.5888H79.203H111.994C112.435 87.4203 112.876 86.6718 112.876 85.6239C112.788 84.5761 112.435 83.8276 111.994 83.8276Z" fill="#525252"/> <path d="M111.995 83.8276H79.2032H75.5891H64.6586C64.2178 83.8276 63.7771 84.5761 63.7771 85.6239C63.7771 86.5221 64.1297 87.4203 64.6586 87.4203H75.5891H79.2032H111.995C112.435 87.4203 112.876 86.6718 112.876 85.6239C112.788 84.5761 112.435 83.8276 111.995 83.8276Z" fill="#575757"/>
<path d="M111.993 74.2476H99.8064H95.6559H64.6599C64.2184 74.2476 63.7769 74.996 63.7769 76.0439C63.7769 76.942 64.1301 77.8402 64.6599 77.8402H95.6559H99.8064H111.993C112.434 77.8402 112.876 77.0917 112.876 76.0439C112.788 74.996 112.434 74.2476 111.993 74.2476Z" fill="#525252"/> <path d="M111.993 74.2476H99.8067H95.6562H64.6602C64.2186 74.2476 63.7771 74.996 63.7771 76.0439C63.7771 76.942 64.1303 77.8402 64.6602 77.8402H95.6562H99.8067H111.993C112.435 77.8402 112.876 77.0917 112.876 76.0439C112.788 74.996 112.435 74.2476 111.993 74.2476Z" fill="#575757"/>
<path d="M113.251 65.8647H110.007H106.762H64.678C64.2274 65.8647 63.7769 66.6132 63.7769 67.6611C63.7769 68.5592 64.1373 69.4574 64.678 69.4574H106.762H110.367H113.161C113.611 69.4574 114.062 68.7089 114.062 67.6611C114.152 66.7629 113.701 65.8647 113.251 65.8647Z" fill="#525252"/> <path d="M113.251 65.8647H110.007H106.763H64.6783C64.2277 65.8647 63.7771 66.6132 63.7771 67.6611C63.7771 68.5592 64.1376 69.4574 64.6783 69.4574H106.763H110.367H113.161C113.612 69.4574 114.062 68.7089 114.062 67.6611C114.152 66.7629 113.702 65.8647 113.251 65.8647Z" fill="#575757"/>
<path d="M113.18 56.2844H102.728H100.137H64.6702C64.2235 56.2844 63.7769 57.0329 63.7769 58.0807C63.7769 58.9789 64.1342 59.877 64.6702 59.877H100.137H102.728H113.091C113.716 59.877 114.073 59.1286 114.073 58.0807C114.073 57.1826 113.716 56.2844 113.18 56.2844Z" fill="#525252"/> <path d="M113.18 56.2844H102.728H100.137H64.6705C64.2238 56.2844 63.7771 57.0329 63.7771 58.0807C63.7771 58.9789 64.1344 59.877 64.6705 59.877H100.137H102.728H113.091C113.716 59.877 114.074 59.1286 114.074 58.0807C114.074 57.1826 113.716 56.2844 113.18 56.2844Z" fill="#575757"/>
<path d="M111.37 8.91162V19.7872C111.37 23.1009 114.056 25.7872 117.37 25.7872H128.187" fill="#181818"/> <path d="M111.37 8.91162V19.7872C111.37 23.1009 114.056 25.7872 117.37 25.7872H128.187" fill="#0E0E0E"/>
<path d="M111.37 8.91162V19.7872C111.37 23.1009 114.056 25.7872 117.37 25.7872H128.187" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M111.37 8.91162V19.7872C111.37 23.1009 114.056 25.7872 117.37 25.7872H128.187" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M70.8988 42.4288V79.5311C70.8988 83.8729 67.3447 87.0305 63.3956 87.0305H61.421V96.8981C61.421 99.2664 58.6567 100.056 57.077 98.477L47.2043 87.0305H8.50327C4.15928 87.0305 1 83.4782 1 79.5311V42.4288C1 38.0871 4.55418 34.9294 8.50327 34.9294H63.3956C67.3447 34.9294 70.8988 38.4818 70.8988 42.4288Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M70.8988 42.4288V79.5311C70.8988 83.8729 67.3447 87.0305 63.3956 87.0305H61.421V96.8981C61.421 99.2664 58.6567 100.056 57.077 98.477L47.2043 87.0305H8.50327C4.15928 87.0305 1 83.4782 1 79.5311V42.4288C1 38.0871 4.55418 34.9294 8.50327 34.9294H63.3956C67.3447 34.9294 70.8988 38.4818 70.8988 42.4288Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M19.5605 61.7695V53.8754C19.5605 52.6913 20.3504 51.9019 21.5351 51.9019H30.2231C31.4078 51.9019 32.1976 52.6913 32.1976 53.8754V62.9536C32.1976 65.3218 32.1976 67.2953 31.8027 68.8742C31.4157 70.4216 30.6493 71.2107 29.5035 72.3563C29.457 72.4028 29.4061 72.4451 29.3504 72.4803C28.3636 73.1039 26.8665 73.4756 25.0679 73.8037C24.46 73.9146 23.9046 73.4429 23.9046 72.825V71.3204C23.9046 70.8227 24.274 70.4097 24.7555 70.2839C26.9907 69.7001 28.2485 68.1396 28.2485 65.3218C28.2485 65.3218 28.2485 65.3218 28.2485 65.3218V65.3218C28.2485 65.3218 27.8951 64.5324 27.4591 64.5324H22.2327C22.0353 64.5324 21.8422 64.4743 21.6783 64.3643C20.5832 63.629 19.5605 62.8696 19.5605 61.7695ZM38.9111 61.7695V53.8754C38.9111 52.6913 39.7009 51.9019 40.8856 51.9019H49.5736C50.7583 51.9019 51.5481 52.6913 51.5481 53.8754V62.9536C51.5481 65.3218 51.5481 67.2953 51.1532 68.8742C50.7662 70.4216 49.9998 71.2107 48.8541 72.3563C48.8075 72.4028 48.7566 72.4451 48.7009 72.4803C47.7141 73.1039 46.217 73.4756 44.4184 73.8037C43.8105 73.9146 43.255 73.4429 43.255 72.825V71.3204C43.255 70.8227 43.6244 70.4097 44.106 70.2839C46.3411 69.7001 47.5991 68.1396 47.5991 65.3218C47.5991 65.3218 47.5991 65.3218 47.5991 65.3218V65.3218C47.5991 65.3218 47.2456 64.5324 46.8097 64.5324H41.5832C41.3858 64.5324 41.1927 64.4743 41.0288 64.3643C39.9337 63.629 38.9111 62.8696 38.9111 61.7695Z" fill="#525252"/> <path d="M19.5605 61.7695V53.8754C19.5605 52.6913 20.3504 51.9019 21.5351 51.9019H30.2231C31.4078 51.9019 32.1976 52.6913 32.1976 53.8754V62.9536C32.1976 65.3218 32.1976 67.2954 31.8027 68.8742C31.4157 70.4216 30.6493 71.2107 29.5035 72.3563C29.457 72.4028 29.4061 72.4451 29.3504 72.4803C28.3636 73.1039 26.8665 73.4756 25.0679 73.8037C24.46 73.9146 23.9046 73.4429 23.9046 72.825V71.3204C23.9046 70.8227 24.274 70.4097 24.7555 70.2839C26.9907 69.7001 28.2485 68.1396 28.2485 65.3218C28.2485 65.3218 27.8951 64.5324 27.4591 64.5324H22.2327C22.0353 64.5324 21.8422 64.4743 21.6783 64.3643C20.5832 63.629 19.5605 62.8696 19.5605 61.7695ZM38.9111 61.7695V53.8754C38.9111 52.6913 39.7009 51.9019 40.8856 51.9019H49.5736C50.7583 51.9019 51.5481 52.6913 51.5481 53.8754V62.9536C51.5481 65.3218 51.5481 67.2954 51.1532 68.8742C50.7662 70.4216 49.9998 71.2107 48.8541 72.3563C48.8075 72.4028 48.7566 72.4451 48.7009 72.4803C47.7141 73.1039 46.217 73.4756 44.4184 73.8037C43.8105 73.9146 43.255 73.4429 43.255 72.825V71.3204C43.255 70.8227 43.6244 70.4097 44.106 70.2839C46.3411 69.7001 47.5991 68.1396 47.5991 65.3218C47.5991 65.3218 47.2456 64.5324 46.8097 64.5324H41.5832C41.3858 64.5324 41.1927 64.4743 41.0288 64.3643C39.9337 63.629 38.9111 62.8696 38.9111 61.7695Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,26 +1,28 @@
<svg width="165" height="142" viewBox="0 0 165 142" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="165" height="142" viewBox="0 0 165 142" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M85.3227 123.613C119.114 123.613 146.509 96.2184 146.509 62.3067C146.509 28.3949 118.993 1 85.3227 1C51.5316 1 24.1367 28.3949 24.1367 62.3067C24.1367 96.2184 51.5316 123.613 85.3227 123.613Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M85.3227 123.613C119.114 123.613 146.509 96.2184 146.509 62.3067C146.509 28.3949 118.993 1 85.3227 1C51.5316 1 24.1367 28.3949 24.1367 62.3067C24.1367 96.2184 51.5316 123.613 85.3227 123.613Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M154.276 42.8823C157.009 42.8823 159.224 40.667 159.224 37.9343C159.224 35.2016 157.009 32.9863 154.276 32.9863C151.543 32.9863 149.328 35.2016 149.328 37.9343C149.328 40.667 151.543 42.8823 154.276 42.8823Z" fill="#181818"/> <path d="M154.276 42.8823C157.009 42.8823 159.224 40.667 159.224 37.9343C159.224 35.2016 157.009 32.9863 154.276 32.9863C151.543 32.9863 149.328 35.2016 149.328 37.9343C149.328 40.667 151.543 42.8823 154.276 42.8823Z" fill="#0E0E0E"/>
<path d="M161.516 23.5734C163.383 23.5734 164.895 22.0605 164.895 20.1943C164.895 18.3281 163.383 16.8152 161.516 16.8152C159.65 16.8152 158.137 18.3281 158.137 20.1943C158.137 22.0605 159.65 23.5734 161.516 23.5734Z" fill="#181818"/> <path d="M161.517 23.5734C163.383 23.5734 164.896 22.0605 164.896 20.1943C164.896 18.3281 163.383 16.8152 161.517 16.8152C159.65 16.8152 158.137 18.3281 158.137 20.1943C158.137 22.0605 159.65 23.5734 161.517 23.5734Z" fill="#0E0E0E"/>
<path d="M26.9123 22.1193C28.7785 22.1193 30.2914 20.6064 30.2914 18.7402C30.2914 16.874 28.7785 15.3611 26.9123 15.3611C25.0461 15.3611 23.5332 16.874 23.5332 18.7402C23.5332 20.6064 25.0461 22.1193 26.9123 22.1193Z" fill="#181818"/> <path d="M26.9123 22.1193C28.7785 22.1193 30.2914 20.6064 30.2914 18.7402C30.2914 16.874 28.7785 15.3611 26.9123 15.3611C25.0461 15.3611 23.5332 16.874 23.5332 18.7402C23.5332 20.6064 25.0461 22.1193 26.9123 22.1193Z" fill="#0E0E0E"/>
<path d="M6.27549 87.288C9.74134 87.288 12.551 84.4784 12.551 81.0126C12.551 77.5467 9.74134 74.7371 6.27549 74.7371C2.80963 74.7371 0 77.5467 0 81.0126C0 84.4784 2.80963 87.288 6.27549 87.288Z" fill="#181818"/> <path d="M6.27549 87.288C9.74134 87.288 12.551 84.4784 12.551 81.0126C12.551 77.5467 9.74134 74.7371 6.27549 74.7371C2.80963 74.7371 0 77.5467 0 81.0126C0 84.4784 2.80963 87.288 6.27549 87.288Z" fill="#0E0E0E"/>
<path d="M121.099 107.854H138.619C140.82 107.854 142.706 105.968 142.706 103.769V22.0724C142.706 19.8729 140.82 17.9875 138.619 17.9875H121.643" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M121.099 107.854H138.619C140.82 107.854 142.706 105.968 142.706 103.769V22.0724C142.706 19.8729 140.82 17.9875 138.619 17.9875H121.643" fill="#0E0E0E"/>
<path d="M51.5361 107.854H34.5596C32.3589 107.854 30.4727 105.968 30.4727 103.769V22.0724C30.4727 19.8729 32.3589 17.9875 34.5596 17.9875H51.1864" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M121.099 107.854H138.619C140.82 107.854 142.706 105.968 142.706 103.769V22.0724C142.706 19.8729 140.82 17.9875 138.619 17.9875H121.643L121.099 107.854Z" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 34.3269C41.2832 33.7746 41.7309 33.3269 42.2832 33.3269H50.7714C51.3237 33.3269 51.7714 33.7746 51.7714 34.3269C51.7714 34.8792 51.3237 35.3269 50.7714 35.3269H42.2832C41.7309 35.3269 41.2832 34.8792 41.2832 34.3269Z" fill="#525252"/> <path d="M51.5361 107.854H34.5596C32.3589 107.854 30.4727 105.968 30.4727 103.769V22.0724C30.4727 19.8729 32.3589 17.9875 34.5596 17.9875H51.1864" fill="#0E0E0E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 45.3245C41.2832 44.7722 41.7309 44.3245 42.2832 44.3245H50.7714C51.3237 44.3245 51.7714 44.7722 51.7714 45.3245C51.7714 45.8767 51.3237 46.3245 50.7714 46.3245H42.2832C41.7309 46.3245 41.2832 45.8767 41.2832 45.3245Z" fill="#525252"/> <path d="M51.5361 107.854H34.5596C32.3589 107.854 30.4727 105.968 30.4727 103.769V22.0724C30.4727 19.8729 32.3589 17.9875 34.5596 17.9875H51.1864L51.5361 107.854Z" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 56.6365C41.2832 56.0842 41.7309 55.6365 42.2832 55.6365H50.7714C51.3237 55.6365 51.7714 56.0842 51.7714 56.6365C51.7714 57.1888 51.3237 57.6365 50.7714 57.6365H42.2832C41.7309 57.6365 41.2832 57.1888 41.2832 56.6365Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 34.3269C41.2832 33.7746 41.7309 33.3269 42.2832 33.3269H50.7714C51.3237 33.3269 51.7714 33.7746 51.7714 34.3269C51.7714 34.8792 51.3237 35.3269 50.7714 35.3269H42.2832C41.7309 35.3269 41.2832 34.8792 41.2832 34.3269Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 67.9485C41.2832 67.3962 41.7309 66.9485 42.2832 66.9485H50.7714C51.3237 66.9485 51.7714 67.3962 51.7714 67.9485C51.7714 68.5008 51.3237 68.9485 50.7714 68.9485H42.2832C41.7309 68.9485 41.2832 68.5008 41.2832 67.9485Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 45.3245C41.2832 44.7722 41.7309 44.3245 42.2832 44.3245H50.7714C51.3237 44.3245 51.7714 44.7722 51.7714 45.3245C51.7714 45.8767 51.3237 46.3245 50.7714 46.3245H42.2832C41.7309 46.3245 41.2832 45.8767 41.2832 45.3245Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 89.9434C41.2832 89.3911 41.7309 88.9434 42.2832 88.9434H50.7714C51.3237 88.9434 51.7714 89.3911 51.7714 89.9434C51.7714 90.4956 51.3237 90.9434 50.7714 90.9434H42.2832C41.7309 90.9434 41.2832 90.4956 41.2832 89.9434Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 56.6365C41.2832 56.0842 41.7309 55.6365 42.2832 55.6365H50.7714C51.3237 55.6365 51.7714 56.0842 51.7714 56.6365C51.7714 57.1888 51.3237 57.6365 50.7714 57.6365H42.2832C41.7309 57.6365 41.2832 57.1888 41.2832 56.6365Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 34.3269C120.643 33.7746 121.09 33.3269 121.643 33.3269H129.816C130.369 33.3269 130.816 33.7746 130.816 34.3269C130.816 34.8792 130.369 35.3269 129.816 35.3269H121.643C121.09 35.3269 120.643 34.8792 120.643 34.3269Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 67.9485C41.2832 67.3962 41.7309 66.9485 42.2832 66.9485H50.7714C51.3237 66.9485 51.7714 67.3962 51.7714 67.9485C51.7714 68.5008 51.3237 68.9485 50.7714 68.9485H42.2832C41.7309 68.9485 41.2832 68.5008 41.2832 67.9485Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 45.3245C120.643 44.7722 121.09 44.3245 121.643 44.3245H129.816C130.369 44.3245 130.816 44.7722 130.816 45.3245C130.816 45.8767 130.369 46.3245 129.816 46.3245H121.643C121.09 46.3245 120.643 45.8767 120.643 45.3245Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M41.2832 89.9434C41.2832 89.3911 41.7309 88.9434 42.2832 88.9434H50.7714C51.3237 88.9434 51.7714 89.3911 51.7714 89.9434C51.7714 90.4956 51.3237 90.9434 50.7714 90.9434H42.2832C41.7309 90.9434 41.2832 90.4956 41.2832 89.9434Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 56.6365C120.643 56.0842 121.09 55.6365 121.643 55.6365H129.816C130.369 55.6365 130.816 56.0842 130.816 56.6365C130.816 57.1888 130.369 57.6365 129.816 57.6365H121.643C121.09 57.6365 120.643 57.1888 120.643 56.6365Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 34.3269C120.643 33.7746 121.09 33.3269 121.643 33.3269H129.816C130.369 33.3269 130.816 33.7746 130.816 34.3269C130.816 34.8792 130.369 35.3269 129.816 35.3269H121.643C121.09 35.3269 120.643 34.8792 120.643 34.3269Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 67.9485C120.643 67.3962 121.09 66.9485 121.643 66.9485H129.816C130.369 66.9485 130.816 67.3962 130.816 67.9485C130.816 68.5008 130.369 68.9485 129.816 68.9485H121.643C121.09 68.9485 120.643 68.5008 120.643 67.9485Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 45.3245C120.643 44.7722 121.09 44.3245 121.643 44.3245H129.816C130.369 44.3245 130.816 44.7722 130.816 45.3245C130.816 45.8767 130.369 46.3245 129.816 46.3245H121.643C121.09 46.3245 120.643 45.8767 120.643 45.3245Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 89.9434C120.643 89.3911 121.09 88.9434 121.643 88.9434H129.816C130.369 88.9434 130.816 89.3911 130.816 89.9434C130.816 90.4956 130.369 90.9434 129.816 90.9434H121.643C121.09 90.9434 120.643 90.4956 120.643 89.9434Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 56.6365C120.643 56.0842 121.09 55.6365 121.643 55.6365H129.816C130.369 55.6365 130.816 56.0842 130.816 56.6365C130.816 57.1888 130.369 57.6365 129.816 57.6365H121.643C121.09 57.6365 120.643 57.1888 120.643 56.6365Z" fill="#575757"/>
<path d="M117.556 114.767H55.3086C53.108 114.767 51.2217 112.881 51.2217 110.682V15.7882C51.2217 13.5887 53.108 11.7034 55.3086 11.7034H117.556C119.756 11.7034 121.643 13.5887 121.643 15.7882V110.682C121.643 112.881 119.756 114.767 117.556 114.767Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 67.9485C120.643 67.3962 121.09 66.9485 121.643 66.9485H129.816C130.369 66.9485 130.816 67.3962 130.816 67.9485C130.816 68.5008 130.369 68.9485 129.816 68.9485H121.643C121.09 68.9485 120.643 68.5008 120.643 67.9485Z" fill="#575757"/>
<path d="M110.01 42.4467H66.6261C65.683 42.4467 65.0542 41.8773 65.0542 41.0231V37.3216C65.0542 36.4674 65.683 35.8979 66.6261 35.8979H110.01C110.954 35.8979 111.582 36.4674 111.582 37.3216V41.0231C111.582 41.5925 110.954 42.4467 110.01 42.4467Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M120.643 89.9434C120.643 89.3911 121.09 88.9434 121.643 88.9434H129.816C130.369 88.9434 130.816 89.3911 130.816 89.9434C130.816 90.4956 130.369 90.9434 129.816 90.9434H121.643C121.09 90.9434 120.643 90.4956 120.643 89.9434Z" fill="#575757"/>
<path d="M110.01 59.4145H66.6261C65.683 59.4145 65.0542 58.845 65.0542 57.9909V54.2894C65.0542 53.4352 65.683 52.8657 66.6261 52.8657H110.01C110.954 52.8657 111.582 53.4352 111.582 54.2894V57.9909C111.582 58.845 110.954 59.4145 110.01 59.4145Z" fill="#525252"/> <path d="M117.556 114.767H55.3086C53.108 114.767 51.2217 112.881 51.2217 110.682V15.7882C51.2217 13.5887 53.108 11.7034 55.3086 11.7034H117.556C119.756 11.7034 121.643 13.5887 121.643 15.7882V110.682C121.643 112.881 119.756 114.767 117.556 114.767Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M110.01 76.6962H66.6261C65.683 76.6962 65.0542 76.1268 65.0542 75.2726V71.5711C65.0542 70.7169 65.683 70.1475 66.6261 70.1475H110.01C110.954 70.1475 111.582 70.7169 111.582 71.5711V75.2726C111.582 76.1268 110.954 76.6962 110.01 76.6962Z" fill="#525252"/> <path d="M110.01 42.4467H66.6261C65.683 42.4467 65.0542 41.8773 65.0542 41.0231V37.3216C65.0542 36.4674 65.683 35.8979 66.6261 35.8979H110.01C110.954 35.8979 111.582 36.4674 111.582 37.3216V41.0231C111.582 41.5925 110.954 42.4467 110.01 42.4467Z" fill="#575757"/>
<path d="M110.011 94.2924H92.7198C91.7767 94.2924 91.1479 93.723 91.1479 92.8688V89.1673C91.1479 88.3131 91.7767 87.7437 92.7198 87.7437H110.011C110.954 87.7437 111.583 88.3131 111.583 89.1673V92.8688C111.583 93.723 110.954 94.2924 110.011 94.2924Z" fill="#525252"/> <path d="M110.01 59.4145H66.6261C65.683 59.4145 65.0542 58.845 65.0542 57.9909V54.2894C65.0542 53.4352 65.683 52.8657 66.6261 52.8657H110.01C110.954 52.8657 111.582 53.4352 111.582 54.2894V57.9909C111.582 58.845 110.954 59.4145 110.01 59.4145Z" fill="#575757"/>
<path d="M105.548 125.569C105.155 127.237 104.566 129.003 103.879 130.475C102.015 134.106 99.0712 136.952 95.4405 138.816C91.7116 140.681 87.2959 141.466 82.8801 140.484C72.4786 138.326 65.8059 128.12 67.9647 117.719C70.1235 107.317 80.2307 100.546 90.6322 102.803C94.3611 103.588 97.5993 105.453 100.347 108.004C104.959 112.616 106.921 119.289 105.548 125.569Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M110.01 76.6962H66.6261C65.683 76.6962 65.0542 76.1268 65.0542 75.2726V71.5711C65.0542 70.7169 65.683 70.1475 66.6261 70.1475H110.01C110.954 70.1475 111.582 70.7169 111.582 71.5711V75.2726C111.582 76.1268 110.954 76.6962 110.01 76.6962Z" fill="#575757"/>
<path d="M92.8892 119.976H88.4735V115.56C88.4735 114.677 87.7866 113.892 86.8053 113.892C85.9222 113.892 85.1371 114.579 85.1371 115.56V119.976H80.7214C79.8382 119.976 79.0532 120.662 79.0532 121.644C79.0532 122.625 79.7401 123.312 80.7214 123.312H85.1371V127.728C85.1371 128.611 85.824 129.396 86.8053 129.396C87.6885 129.396 88.4735 128.709 88.4735 127.728V123.312H92.8892C93.7724 123.312 94.5574 122.625 94.5574 121.644C94.5574 120.662 93.7724 119.976 92.8892 119.976Z" fill="#525252"/> <path d="M110.011 94.2924H92.7201C91.777 94.2924 91.1482 93.723 91.1482 92.8688V89.1673C91.1482 88.3131 91.777 87.7437 92.7201 87.7437H110.011C110.954 87.7437 111.583 88.3131 111.583 89.1673V92.8688C111.583 93.723 110.954 94.2924 110.011 94.2924Z" fill="#575757"/>
<path d="M105.547 125.569C105.155 127.237 104.566 129.003 103.879 130.475C102.015 134.106 99.071 136.952 95.4402 138.816C91.7114 140.681 87.2956 141.466 82.8799 140.484C72.4783 138.326 65.8057 128.12 67.9645 117.719C70.1233 107.317 80.2304 100.546 90.632 102.803C94.3608 103.588 97.5991 105.453 100.347 108.004C104.959 112.616 106.921 119.289 105.547 125.569Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M92.889 119.976H88.4732V115.56C88.4732 114.677 87.7864 113.892 86.8051 113.892C85.9219 113.892 85.1369 114.579 85.1369 115.56V119.976H80.7212C79.838 119.976 79.053 120.662 79.053 121.644C79.053 122.625 79.7399 123.312 80.7212 123.312H85.1369V127.728C85.1369 128.611 85.8238 129.396 86.8051 129.396C87.6882 129.396 88.4732 128.709 88.4732 127.728V123.312H92.889C93.7721 123.312 94.5572 122.625 94.5572 121.644C94.5572 120.662 93.7721 119.976 92.889 119.976Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,37 +1,37 @@
<svg width="117" height="120" viewBox="0 0 117 120" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="117" height="120" viewBox="0 0 117 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M104.634 68.6831C104.634 80.1972 100.53 90.6702 93.6709 98.8771C85.0353 109.228 72.0513 115.781 57.4749 115.781C43.511 115.781 31.0169 109.717 22.3813 100.102C14.9706 91.7726 10.4385 80.7484 10.4385 68.6831C10.4385 42.6538 31.5069 21.5854 57.5361 21.5854C83.5654 21.5854 104.634 42.6538 104.634 68.6831Z" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4 4"/> <path d="M104.634 68.6831C104.634 80.1972 100.53 90.6702 93.6709 98.8771C85.0353 109.228 72.0513 115.781 57.4749 115.781C43.511 115.781 31.0169 109.717 22.3813 100.102C14.9706 91.7726 10.4385 80.7484 10.4385 68.6831C10.4385 42.6538 31.5069 21.5854 57.5361 21.5854C83.5654 21.5854 104.634 42.6538 104.634 68.6831Z" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4 4"/>
<path d="M74.8781 71.7725C74.5226 73.2834 73.9893 74.8833 73.3671 76.2164C71.6785 79.5049 69.0121 82.0824 65.7236 83.7711C62.3462 85.4598 58.3467 86.1708 54.3471 85.282C44.926 83.3267 38.8823 74.0833 40.8376 64.6622C42.7929 55.2411 51.9474 49.1085 61.3685 51.1527C64.7459 51.8637 67.6789 53.5524 70.1675 55.8632C74.3448 60.0405 76.1224 66.0843 74.8781 71.7725Z" fill="#525252" stroke="#737373" stroke-width="2" stroke-miterlimit="10"/> <path d="M74.8781 71.7725C74.5226 73.2834 73.9893 74.8833 73.3671 76.2164C71.6785 79.5049 69.0121 82.0824 65.7236 83.7711C62.3462 85.4598 58.3467 86.1708 54.3471 85.282C44.926 83.3267 38.8823 74.0833 40.8376 64.6622C42.7929 55.2411 51.9474 49.1085 61.3685 51.1527C64.7459 51.8637 67.6789 53.5524 70.1675 55.8632C74.3448 60.0405 76.1224 66.0843 74.8781 71.7725Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10"/>
<path d="M63.4127 66.7063H60.4132C59.8609 66.7063 59.4132 66.2586 59.4132 65.7063V62.7067C59.4132 61.9068 58.791 61.1958 57.9023 61.1958C57.1024 61.1958 56.3913 61.818 56.3913 62.7067V65.7063C56.3913 66.2586 55.9436 66.7063 55.3913 66.7063H52.3918C51.5919 66.7063 50.8809 67.3284 50.8809 68.2172C50.8809 69.106 51.503 69.7281 52.3918 69.7281H55.3913C55.9436 69.7281 56.3913 70.1759 56.3913 70.7281V73.7277C56.3913 74.5276 57.0135 75.2386 57.9023 75.2386C58.7022 75.2386 59.4132 74.6165 59.4132 73.7277V70.7281C59.4132 70.1759 59.8609 69.7281 60.4132 69.7281H63.4127C64.2126 69.7281 64.9237 69.106 64.9237 68.2172C64.9237 67.3284 64.2126 66.7063 63.4127 66.7063Z" fill="#525252"/> <path d="M63.4127 66.7063H60.4132C59.8609 66.7063 59.4132 66.2586 59.4132 65.7063V62.7067C59.4132 61.9068 58.791 61.1958 57.9023 61.1958C57.1024 61.1958 56.3913 61.818 56.3913 62.7067V65.7063C56.3913 66.2586 55.9436 66.7063 55.3913 66.7063H52.3918C51.5919 66.7063 50.8809 67.3284 50.8809 68.2172C50.8809 69.106 51.503 69.7281 52.3918 69.7281H55.3913C55.9436 69.7281 56.3913 70.1759 56.3913 70.7281V73.7277C56.3913 74.5276 57.0135 75.2386 57.9023 75.2386C58.7022 75.2386 59.4132 74.6165 59.4132 73.7277V70.7281C59.4132 70.1759 59.8609 69.7281 60.4132 69.7281H63.4127C64.2126 69.7281 64.9237 69.106 64.9237 68.2172C64.9237 67.3284 64.2126 66.7063 63.4127 66.7063Z" fill="#575757"/>
<path d="M78.3516 21.6057C78.3516 27.1603 76.1318 32.0976 72.6787 35.8007C72.4321 36.171 72.0621 36.4178 71.6921 36.6647C67.9924 40.1209 63.0594 42.2192 57.6332 42.2192C53.3169 42.2192 49.2472 40.8615 45.9174 38.5162C44.9308 37.899 44.0675 37.035 43.2043 36.2944C39.3812 32.5914 37.0381 27.4071 37.0381 21.6057C37.0381 10.2498 46.2874 0.992188 57.6332 0.992188C69.1023 0.992188 78.3516 10.2498 78.3516 21.6057Z" fill="black"/> <path d="M78.3516 21.6057C78.3516 27.1603 76.1318 32.0976 72.6787 35.8007C72.4321 36.171 72.0621 36.4178 71.6921 36.6647C67.9924 40.1209 63.0594 42.2192 57.6332 42.2192C53.3169 42.2192 49.2472 40.8615 45.9174 38.5162C44.9308 37.899 44.0675 37.035 43.2043 36.2944C39.3812 32.5914 37.0381 27.4071 37.0381 21.6057C37.0381 10.2498 46.2874 0.992188 57.6332 0.992188C69.1023 0.992188 78.3516 10.2498 78.3516 21.6057Z" fill="black"/>
<path d="M78.3516 21.6043C78.3516 27.6526 75.7618 32.9603 71.6921 36.7868C67.9924 40.2429 63.0594 42.3413 57.6332 42.3413C53.3169 42.3413 49.2472 40.9835 45.9174 38.6383C40.4912 34.9353 37.0381 28.7635 37.0381 21.7278C37.0381 10.3718 46.2874 1.11426 57.6332 1.11426C68.979 1.11426 78.3516 10.2484 78.3516 21.6043Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M78.3516 21.6043C78.3516 27.6526 75.7618 32.9603 71.6921 36.7868C67.9924 40.2429 63.0594 42.3413 57.6332 42.3413C53.3169 42.3413 49.2472 40.9835 45.9174 38.6383C40.4912 34.9353 37.0381 28.7635 37.0381 21.7278C37.0381 10.3718 46.2874 1.11426 57.6332 1.11426C68.979 1.11426 78.3516 10.2484 78.3516 21.6043Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M72.679 35.8005C72.4324 36.1708 72.0624 36.4176 71.6924 36.6645C67.9927 40.1207 63.0597 42.219 57.6335 42.219C53.3172 42.219 49.2475 40.8613 45.9177 38.516C44.9311 37.8988 44.0679 37.0348 43.2046 36.2942C43.6979 35.677 44.4378 35.3067 46.041 34.6895L46.6577 34.4427C47.8909 33.9489 49.6174 33.3318 51.8373 32.3443C52.2072 32.2209 52.4539 31.974 52.7005 31.7271C52.8239 31.6037 52.9472 31.4803 52.9472 31.2334C53.0705 30.9865 53.1938 30.6162 53.1938 30.3693V26.1726C53.0705 26.0492 53.0705 26.0492 52.9472 25.9257C52.5772 25.432 52.3306 24.8148 52.3306 24.0742L52.0839 23.9508C50.974 24.1976 51.0973 23.0867 50.8507 20.8649C50.7274 20.0009 50.8507 19.754 51.344 19.6306L51.7139 19.1368C50.974 17.4088 50.604 15.8041 50.604 14.5698C50.604 12.4714 51.4673 11.1136 52.7005 10.4964C51.9606 9.01522 51.9606 8.52148 51.9606 8.52148C51.9606 8.52148 56.2769 9.26209 57.7568 9.01522C59.6067 8.64492 62.5664 9.13866 63.6764 11.6073C65.5262 12.3479 66.1428 13.4589 66.3895 14.6932C66.6361 16.6681 65.5262 18.7665 65.2796 19.6306V19.754C65.5262 19.8774 65.6495 20.1243 65.5262 20.9883C65.2796 23.0867 65.2796 24.3211 64.293 24.0742L63.3064 25.8023C63.3064 26.0492 63.3064 26.0492 63.1831 26.1726C63.1831 26.5429 63.1831 27.1601 63.1831 30.4928C63.1831 30.8631 63.3064 31.3568 63.553 31.6037C63.6764 31.7271 63.6764 31.8506 63.7997 31.8506C64.0463 32.0974 64.293 32.3443 64.5396 32.3443C67.0061 33.3318 68.7326 34.0724 70.0892 34.5661C71.3224 35.0599 72.1857 35.4302 72.679 35.8005Z" fill="#181818"/> <path d="M72.679 35.8005C72.4324 36.1708 72.0624 36.4176 71.6924 36.6645C67.9927 40.1207 63.0597 42.219 57.6335 42.219C53.3172 42.219 49.2475 40.8613 45.9177 38.516C44.9311 37.8988 44.0679 37.0348 43.2046 36.2942C43.6979 35.677 44.4378 35.3067 46.041 34.6895L46.6577 34.4427C47.8909 33.9489 49.6174 33.3318 51.8373 32.3443C52.2072 32.2209 52.4539 31.974 52.7005 31.7271C52.8239 31.6037 52.9472 31.4803 52.9472 31.2334C53.0705 30.9865 53.1938 30.6162 53.1938 30.3693V26.1726C53.0705 26.0492 53.0705 26.0492 52.9472 25.9257C52.5772 25.432 52.3306 24.8148 52.3306 24.0742L52.0839 23.9508C50.974 24.1976 51.0973 23.0867 50.8507 20.8649C50.7274 20.0009 50.8507 19.754 51.344 19.6306L51.7139 19.1368C50.974 17.4088 50.604 15.8041 50.604 14.5698C50.604 12.4714 51.4673 11.1136 52.7005 10.4964C51.9606 9.01522 51.9606 8.52148 51.9606 8.52148C51.9606 8.52148 56.2769 9.26209 57.7568 9.01522C59.6067 8.64492 62.5664 9.13866 63.6764 11.6073C65.5262 12.3479 66.1428 13.4589 66.3895 14.6932C66.6361 16.6681 65.5262 18.7665 65.2796 19.6306V19.754C65.5262 19.8774 65.6495 20.1243 65.5262 20.9883C65.2796 23.0867 65.2796 24.3211 64.293 24.0742L63.3064 25.8023C63.3064 26.0492 63.3064 26.0492 63.1831 26.1726C63.1831 26.5429 63.1831 27.1601 63.1831 30.4928C63.1831 30.8631 63.3064 31.3568 63.553 31.6037C63.6764 31.7271 63.6764 31.8506 63.7997 31.8506C64.0463 32.0974 64.293 32.3443 64.5396 32.3443C67.0061 33.3318 68.7326 34.0724 70.0892 34.5661C71.3224 35.0599 72.1857 35.4302 72.679 35.8005Z" fill="#0E0E0E"/>
<path d="M72.679 35.8004C72.4324 36.1707 72.0624 36.4176 71.6924 36.6644C67.9927 40.1206 63.0597 42.219 57.6335 42.219C53.3172 42.219 49.2475 40.8612 45.9177 38.5159C44.9311 37.8988 44.0679 37.0347 43.2046 36.2941C43.6979 35.6769 44.4378 35.3066 46.041 34.6895L46.6577 34.4426C47.8909 33.9489 49.6174 33.3317 51.8373 32.3442C52.2072 32.2208 52.4539 31.9739 52.7005 31.7271C53.9338 33.4551 55.907 34.566 58.2501 34.566C60.4699 34.566 62.4431 33.4551 63.6764 31.8505C63.923 32.0974 64.1697 32.3442 64.4163 32.3442C66.8828 33.3317 68.6093 34.0723 69.9659 34.566C71.3224 35.0598 72.1857 35.4301 72.679 35.8004Z" fill="#525252"/> <path d="M72.679 35.8004C72.4324 36.1707 72.0624 36.4176 71.6924 36.6644C67.9927 40.1206 63.0597 42.219 57.6335 42.219C53.3172 42.219 49.2475 40.8612 45.9177 38.5159C44.9311 37.8988 44.0679 37.0347 43.2046 36.2941C43.6979 35.6769 44.4378 35.3066 46.041 34.6895L46.6577 34.4426C47.8909 33.9489 49.6174 33.3317 51.8373 32.3442C52.2072 32.2208 52.4539 31.9739 52.7005 31.7271C53.9338 33.4551 55.907 34.566 58.2501 34.566C60.4699 34.566 62.4431 33.4551 63.6764 31.8505C63.923 32.0974 64.1697 32.3442 64.4163 32.3442C66.8828 33.3317 68.6093 34.0723 69.9659 34.566C71.3224 35.0598 72.1857 35.4301 72.679 35.8004Z" fill="#575757"/>
<path d="M65.1564 19.5071C65.2797 19.0134 65.0331 18.2728 64.7864 17.9025C64.7864 17.7791 64.6631 17.7791 64.6631 17.6556C63.7999 15.9275 61.95 15.3104 60.2235 15.1869C55.6605 14.9401 55.2905 15.8041 53.9339 14.5698C54.4272 15.1869 54.4272 16.2978 53.6873 17.5322C53.194 18.3962 52.3307 18.89 51.4675 19.1368C49.371 14.4463 50.4809 11.4839 52.454 10.4964C51.7141 9.01522 51.7141 8.52148 51.7141 8.52148C51.7141 8.52148 56.0304 9.26209 57.5103 9.01522C59.3602 8.64492 62.32 9.13866 63.4299 11.6073C65.2797 12.3479 65.8964 13.4589 66.143 14.6932C66.513 16.5447 65.4031 18.6431 65.1564 19.5071Z" fill="#737373"/> <path d="M65.1562 19.5071C65.2795 19.0134 65.0328 18.2728 64.7862 17.9025C64.7862 17.7791 64.6629 17.7791 64.6629 17.6556C63.7996 15.9275 61.9497 15.3104 60.2232 15.1869C55.6602 14.9401 55.2903 15.8041 53.9337 14.5698C54.427 15.1869 54.427 16.2978 53.687 17.5322C53.1937 18.3962 52.3305 18.89 51.4672 19.1368C49.3707 14.4463 50.4806 11.4839 52.4538 10.4964C51.7139 9.01522 51.7139 8.52148 51.7139 8.52148C51.7139 8.52148 56.0302 9.26209 57.5101 9.01522C59.3599 8.64492 62.3197 9.13866 63.4296 11.6073C65.2795 12.3479 65.8961 13.4589 66.1428 14.6932C66.5127 16.5447 65.4028 18.6431 65.1562 19.5071Z" fill="#545454"/>
<path d="M53.317 30.3692V26.1724C53.1936 26.049 53.1936 26.049 53.0703 25.9256V25.6787C53.317 26.049 53.5636 26.4193 53.9336 26.6662L57.2633 29.0114C58.0033 29.6286 59.1132 29.6286 59.8531 29.0114L62.9362 26.2959C63.0595 26.1724 63.1829 26.1724 63.3062 26.049C63.3062 26.4193 63.3062 27.0365 63.3062 30.3692C63.3062 30.6161 63.3062 30.7395 63.4295 30.9864H53.317C53.1936 30.7395 53.317 30.6161 53.317 30.3692Z" fill="url(#paint0_linear)"/> <path d="M53.3172 30.3692V26.1724C53.1939 26.049 53.1939 26.049 53.0706 25.9256V25.6787C53.3172 26.049 53.5639 26.4193 53.9338 26.6662L57.2636 29.0114C58.0035 29.6286 59.1134 29.6286 59.8534 29.0114L62.9365 26.2959C63.0598 26.1724 63.1831 26.1724 63.3064 26.049C63.3064 26.4193 63.3064 27.0365 63.3064 30.3692C63.3064 30.6161 63.3064 30.7395 63.4298 30.9864H53.3172C53.1939 30.7395 53.3172 30.6161 53.3172 30.3692Z" fill="url(#paint0_linear_592_1141)"/>
<path d="M115.285 97.8074C115.285 103.362 113.065 108.299 109.612 112.002C109.365 112.373 108.995 112.619 108.625 112.866C104.925 116.323 99.9925 118.421 94.5663 118.421C90.25 118.421 86.1803 117.063 82.8505 114.718C81.8639 114.101 81.0007 113.237 80.1374 112.496C76.3143 108.793 73.9712 103.609 73.9712 97.8074C73.9712 86.4514 83.2205 77.1938 94.5663 77.1938C106.035 77.1938 115.285 86.4514 115.285 97.8074Z" fill="black"/> <path d="M115.285 97.8074C115.285 103.362 113.065 108.299 109.612 112.002C109.365 112.373 108.995 112.619 108.625 112.866C104.925 116.323 99.9925 118.421 94.5663 118.421C90.25 118.421 86.1803 117.063 82.8505 114.718C81.8639 114.101 81.0007 113.237 80.1374 112.496C76.3143 108.793 73.9712 103.609 73.9712 97.8074C73.9712 86.4514 83.2205 77.1938 94.5663 77.1938C106.035 77.1938 115.285 86.4514 115.285 97.8074Z" fill="black"/>
<path d="M115.285 97.8065C115.285 103.855 112.695 109.162 108.625 112.989C104.925 116.445 99.9925 118.543 94.5663 118.543C90.25 118.543 86.1803 117.186 82.8505 114.84C77.4243 111.137 73.9712 104.966 73.9712 97.9299C73.9712 86.574 83.2205 77.3164 94.5663 77.3164C105.912 77.3164 115.285 86.4505 115.285 97.8065Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M115.285 97.8065C115.285 103.855 112.695 109.162 108.625 112.989C104.925 116.445 99.9925 118.543 94.5663 118.543C90.25 118.543 86.1803 117.186 82.8505 114.84C77.4243 111.137 73.9712 104.966 73.9712 97.9299C73.9712 86.574 83.2205 77.3164 94.5663 77.3164C105.912 77.3164 115.285 86.4505 115.285 97.8065Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M109.613 112.003C109.366 112.373 108.996 112.62 108.626 112.867C104.927 116.323 99.9938 118.421 94.5676 118.421C90.2512 118.421 86.1815 117.063 82.8518 114.718C81.8652 114.101 81.0019 113.237 80.1387 112.496C80.632 111.879 81.3719 111.509 82.9751 110.892L83.5917 110.645C84.825 110.151 86.5515 109.534 88.7713 108.546C89.1413 108.423 89.388 108.176 89.6346 107.929C89.7579 107.806 89.8813 107.682 89.8813 107.436C90.0046 107.189 90.1279 106.818 90.1279 106.571V102.375C90.0046 102.251 90.0046 102.251 89.8813 102.128C89.5113 101.634 89.2646 101.017 89.2646 100.276L89.018 100.153C87.9081 100.4 88.0314 99.2889 87.7848 97.0671C87.6614 96.203 87.7848 95.9562 88.278 95.8327L88.648 95.339C87.9081 93.6109 87.5381 92.0063 87.5381 90.7719C87.5381 88.6735 88.4014 87.3158 89.6346 86.6986C88.8947 85.2174 88.8947 84.7236 88.8947 84.7236C88.8947 84.7236 93.211 85.4642 94.6909 85.2174C96.5408 84.8471 99.5005 85.3408 100.61 87.8095C102.46 88.5501 103.077 89.661 103.324 90.8953C103.57 92.8703 102.46 94.9687 102.214 95.8327V95.9562C102.46 96.0796 102.584 96.3265 102.46 97.1905C102.214 99.2889 102.214 100.523 101.227 100.276L100.24 102.004C100.24 102.251 100.24 102.251 100.117 102.375C100.117 102.745 100.117 103.362 100.117 106.695C100.117 107.065 100.24 107.559 100.487 107.806C100.61 107.929 100.61 108.053 100.734 108.053C100.98 108.3 101.227 108.546 101.474 108.546C103.94 109.534 105.667 110.275 107.023 110.768C108.257 111.262 109.12 111.632 109.613 112.003Z" fill="#181818"/> <path d="M109.613 112.003C109.366 112.373 108.996 112.62 108.626 112.867C104.927 116.323 99.9936 118.421 94.5673 118.421C90.251 118.421 86.1813 117.063 82.8516 114.718C81.865 114.101 81.0017 113.237 80.1384 112.496C80.6317 111.879 81.3717 111.509 82.9749 110.892L83.5915 110.645C84.8247 110.151 86.5513 109.534 88.7711 108.546C89.1411 108.423 89.3877 108.176 89.6344 107.929C89.7577 107.806 89.881 107.682 89.881 107.436C90.0043 107.189 90.1277 106.818 90.1277 106.571V102.375C90.0043 102.251 90.0043 102.251 89.881 102.128C89.511 101.634 89.2644 101.017 89.2644 100.276L89.0178 100.153C87.9078 100.4 88.0312 99.2889 87.7845 97.0671C87.6612 96.203 87.7845 95.9562 88.2778 95.8327L88.6478 95.339C87.9078 93.6109 87.5379 92.0063 87.5379 90.7719C87.5379 88.6735 88.4011 87.3158 89.6344 86.6986C88.8944 85.2174 88.8944 84.7236 88.8944 84.7236C88.8944 84.7236 93.2108 85.4642 94.6907 85.2174C96.5405 84.8471 99.5003 85.3408 100.61 87.8095C102.46 88.5501 103.077 89.661 103.323 90.8953C103.57 92.8703 102.46 94.9687 102.213 95.8327V95.9562C102.46 96.0796 102.583 96.3265 102.46 97.1905C102.213 99.2889 102.213 100.523 101.227 100.276L100.24 102.004C100.24 102.251 100.24 102.251 100.117 102.375C100.117 102.745 100.117 103.362 100.117 106.695C100.117 107.065 100.24 107.559 100.487 107.806C100.61 107.929 100.61 108.053 100.734 108.053C100.98 108.3 101.227 108.546 101.473 108.546C103.94 109.534 105.666 110.275 107.023 110.768C108.256 111.262 109.12 111.632 109.613 112.003Z" fill="#0E0E0E"/>
<path d="M109.612 112.003C109.365 112.373 108.995 112.62 108.626 112.867C104.926 116.323 99.9928 118.421 94.5666 118.421C90.2503 118.421 86.1806 117.063 82.8508 114.718C81.8642 114.101 81.001 113.237 80.1377 112.496C80.631 111.879 81.3709 111.509 82.9741 110.892L83.5908 110.645C84.824 110.151 86.5505 109.534 88.7704 108.546C89.1403 108.423 89.387 108.176 89.6336 107.929C90.8669 109.657 92.8401 110.768 95.1832 110.768C97.403 110.768 99.3762 109.657 100.609 108.053C100.856 108.3 101.103 108.546 101.349 108.546C103.816 109.534 105.542 110.274 106.899 110.768C108.256 111.262 109.119 111.632 109.612 112.003Z" fill="#525252"/> <path d="M109.612 112.003C109.365 112.373 108.995 112.62 108.626 112.867C104.926 116.323 99.9928 118.421 94.5666 118.421C90.2503 118.421 86.1806 117.063 82.8508 114.718C81.8642 114.101 81.001 113.237 80.1377 112.496C80.631 111.879 81.3709 111.509 82.9741 110.892L83.5908 110.645C84.824 110.151 86.5505 109.534 88.7704 108.546C89.1403 108.423 89.387 108.176 89.6336 107.929C90.8669 109.657 92.8401 110.768 95.1832 110.768C97.403 110.768 99.3762 109.657 100.609 108.053C100.856 108.3 101.103 108.546 101.349 108.546C103.816 109.534 105.542 110.274 106.899 110.768C108.256 111.262 109.119 111.632 109.612 112.003Z" fill="#575757"/>
<path d="M102.09 95.7093C102.213 95.2155 101.966 94.4749 101.72 94.1046C101.72 93.9812 101.596 93.9812 101.596 93.8578C100.733 92.1297 98.8831 91.5125 97.1566 91.3891C92.5936 91.1422 92.2236 92.0063 90.867 90.7719C91.3603 91.3891 91.3603 92.5 90.6204 93.7343C90.1271 94.5984 89.2638 95.0921 88.4006 95.339C86.3041 90.6485 87.414 87.6861 89.3872 86.6986C88.6472 85.2174 88.6472 84.7236 88.6472 84.7236C88.6472 84.7236 92.9635 85.4642 94.4434 85.2174C96.2933 84.8471 99.2531 85.3408 100.363 87.8095C102.213 88.5501 102.829 89.661 103.076 90.8953C103.446 92.7469 102.336 94.8452 102.09 95.7093Z" fill="#737373"/> <path d="M102.089 95.7093C102.213 95.2155 101.966 94.4749 101.719 94.1046C101.719 93.9812 101.596 93.9812 101.596 93.8578C100.733 92.1297 98.8829 91.5125 97.1563 91.3891C92.5933 91.1422 92.2234 92.0063 90.8668 90.7719C91.3601 91.3891 91.3601 92.5 90.6201 93.7343C90.1269 94.5984 89.2636 95.0921 88.4003 95.339C86.3038 90.6485 87.4137 87.6861 89.3869 86.6986C88.647 85.2174 88.647 84.7236 88.647 84.7236C88.647 84.7236 92.9633 85.4642 94.4432 85.2174C96.293 84.8471 99.2528 85.3408 100.363 87.8095C102.213 88.5501 102.829 89.661 103.076 90.8953C103.446 92.7469 102.336 94.8452 102.089 95.7093Z" fill="#545454"/>
<path d="M90.2501 106.571V102.375C90.1267 102.251 90.1267 102.251 90.0034 102.128V101.881C90.2501 102.251 90.4967 102.621 90.8667 102.868L94.1964 105.214C94.9364 105.831 96.0463 105.831 96.7862 105.214L99.8693 102.498C99.9927 102.375 100.116 102.375 100.239 102.251C100.239 102.621 100.239 103.239 100.239 106.571C100.239 106.818 100.239 106.942 100.363 107.189H90.2501C90.1267 106.942 90.2501 106.818 90.2501 106.571Z" fill="url(#paint1_linear)"/> <path d="M90.2501 106.571V102.375C90.1267 102.251 90.1267 102.251 90.0034 102.128V101.881C90.2501 102.251 90.4967 102.621 90.8667 102.868L94.1964 105.214C94.9364 105.831 96.0463 105.831 96.7862 105.214L99.8693 102.498C99.9927 102.375 100.116 102.375 100.239 102.251C100.239 102.621 100.239 103.239 100.239 106.571C100.239 106.818 100.239 106.942 100.363 107.189H90.2501C90.1267 106.942 90.2501 106.818 90.2501 106.571Z" fill="url(#paint1_linear_592_1141)"/>
<path d="M41.2036 98.1168C41.2036 103.918 38.7371 109.102 34.7908 112.805C33.6808 113.793 32.5709 114.657 31.2144 115.398C28.2546 117.126 24.8015 118.113 21.1018 118.113C17.4021 118.113 13.949 117.126 10.9892 115.398C10.4959 115.151 10.126 114.904 9.63268 114.534C4.45307 110.954 1 104.906 1 98.1168C1 87.0077 10.0026 78.1204 20.9785 78.1204C32.201 77.997 41.2036 87.0077 41.2036 98.1168Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M41.2036 98.1168C41.2036 103.918 38.7371 109.102 34.7908 112.805C33.6808 113.793 32.5709 114.657 31.2144 115.398C28.2546 117.126 24.8015 118.113 21.1018 118.113C17.4021 118.113 13.949 117.126 10.9892 115.398C10.4959 115.151 10.126 114.904 9.63268 114.534C4.45307 110.954 1 104.906 1 98.1168C1 87.0077 10.0026 78.1204 20.9785 78.1204C32.201 77.997 41.2036 87.0077 41.2036 98.1168Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M17.0323 102.56C17.279 102.806 17.5256 103.177 17.8956 103.424C18.1422 103.67 18.3889 103.794 18.6355 104.041C18.7589 104.164 19.0055 104.288 19.1288 104.411C19.1288 104.411 19.2522 104.411 19.2522 104.534L19.3755 104.658V106.386C19.3755 106.386 19.3755 106.386 19.2522 106.263C19.1288 106.139 18.8822 106.016 18.7589 105.892C18.5122 105.769 18.2656 105.522 18.0189 105.399C17.8956 105.399 17.8956 105.275 17.7723 105.275C16.909 104.781 16.1691 104.288 16.1691 103.67C16.2924 103.424 16.539 103.053 17.0323 102.56ZM34.1878 112.107C33.4478 110.502 32.4478 108.978 30.7213 108.114C29.858 107.744 28.8714 107.373 27.8848 107.373C27.6382 107.373 27.2682 107.373 27.0216 107.373C26.8982 107.373 26.7749 107.373 26.6516 107.373C25.295 107.25 25.1717 107.003 25.1717 107.003V104.164C26.035 103.424 26.8982 102.56 27.6382 101.695C28.2548 100.831 28.7481 99.844 28.9947 98.6096C30.1047 98.3628 30.8446 97.3753 30.7213 96.1409C30.7213 95.6472 30.3513 95.1535 30.3513 94.6597C30.3513 94.4129 30.3513 94.166 30.3513 93.9191C30.3513 93.7957 30.3513 93.5488 30.3513 93.4254C30.3513 93.3019 30.3513 93.0551 30.3513 92.9316C30.228 92.0676 29.9813 91.2036 29.488 90.2161C28.0082 87.5005 25.295 85.7725 22.0886 85.7725C21.472 85.7725 20.8554 85.8959 20.2387 86.0193C19.1288 86.2662 18.0189 86.7599 17.1556 87.5005C17.0323 87.624 16.7857 87.7474 16.6624 87.9943L16.539 88.1177C15.5524 89.1052 14.6892 90.2161 14.3192 91.5739C13.8259 92.9316 13.8259 94.2894 13.9492 95.6472C13.9492 95.6472 13.9492 95.6472 13.9492 95.7706V95.8941C13.9492 96.1409 14.0726 96.1409 13.9492 96.2644C13.9492 96.3878 13.8259 96.3878 13.8259 96.5112C13.5793 96.8815 13.4559 97.3753 13.7026 98.1159C14.1959 99.3502 14.9358 99.2268 15.7991 99.844C15.7991 99.844 15.6758 99.844 15.6758 99.9674L14.8125 100.214C10.8661 101.449 9.50957 104.781 11.2361 106.88C11.8527 107.62 12.8393 108.238 14.3192 108.608C13.9492 108.608 13.5793 108.855 13.3326 109.102C11.6061 110.459 10.4962 112.558 10.2495 114.533C10.2495 114.656 10.2495 114.78 10.2495 114.903C10.7428 115.15 11.1128 115.52 11.6061 115.767L30.9175 115.315C32.1507 114.575 32.6963 114.003 33.8062 113.016C33.6829 112.399 34.3111 112.23 34.1878 112.107Z" fill="#181818"/> <path d="M17.0321 102.56C17.2787 102.806 17.5254 103.177 17.8954 103.424C18.142 103.67 18.3886 103.794 18.6353 104.041C18.7586 104.164 19.0053 104.288 19.1286 104.411C19.1286 104.411 19.2519 104.411 19.2519 104.534L19.3752 104.658V106.386C19.3752 106.386 19.3752 106.386 19.2519 106.263C19.1286 106.139 18.8819 106.016 18.7586 105.892C18.512 105.769 18.2653 105.522 18.0187 105.399C17.8953 105.399 17.8953 105.275 17.772 105.275C16.9088 104.781 16.1688 104.288 16.1688 103.67C16.2921 103.424 16.5388 103.053 17.0321 102.56ZM34.1875 112.107C33.4476 110.502 32.4476 108.978 30.721 108.114C29.8578 107.744 28.8712 107.373 27.8846 107.373C27.6379 107.373 27.268 107.373 27.0213 107.373C26.898 107.373 26.7747 107.373 26.6513 107.373C25.2948 107.25 25.1715 107.003 25.1715 107.003V104.164C26.0347 103.424 26.898 102.56 27.6379 101.695C28.2546 100.831 28.7479 99.844 28.9945 98.6096C30.1044 98.3628 30.8444 97.3753 30.721 96.1409C30.721 95.6472 30.3511 95.1535 30.3511 94.6597C30.3511 94.4129 30.3511 94.166 30.3511 93.9191C30.3511 93.7957 30.3511 93.5488 30.3511 93.4254C30.3511 93.3019 30.3511 93.0551 30.3511 92.9316C30.2277 92.0676 29.9811 91.2036 29.4878 90.2161C28.0079 87.5005 25.2948 85.7725 22.0884 85.7725C21.4717 85.7725 20.8551 85.8959 20.2385 86.0193C19.1286 86.2662 18.0187 86.7599 17.1554 87.5005C17.0321 87.624 16.7854 87.7474 16.6621 87.9943L16.5388 88.1177C15.5522 89.1052 14.6889 90.2161 14.319 91.5739C13.8257 92.9316 13.8257 94.2894 13.949 95.6472C13.949 95.6472 13.949 95.6472 13.949 95.7706V95.8941C13.949 96.1409 14.0723 96.1409 13.949 96.2644C13.949 96.3878 13.8257 96.3878 13.8257 96.5112C13.579 96.8815 13.4557 97.3753 13.7023 98.1159C14.1956 99.3502 14.9356 99.2268 15.7988 99.844C15.7988 99.844 15.6755 99.844 15.6755 99.9674L14.8123 100.214C10.8659 101.449 9.50932 104.781 11.2359 106.88C11.8525 107.62 12.8391 108.238 14.319 108.608C13.949 108.608 13.579 108.855 13.3324 109.102C11.6058 110.459 10.4959 112.558 10.2493 114.533C10.2493 114.656 10.2493 114.78 10.2493 114.903C10.7426 115.15 11.1125 115.52 11.6058 115.767L30.9172 115.315C32.1505 114.575 32.6961 114.003 33.806 113.016C33.6827 112.399 34.3108 112.23 34.1875 112.107Z" fill="#0E0E0E"/>
<path d="M34.7909 112.805C33.681 113.792 32.5711 114.656 31.2145 115.397C28.2547 117.125 24.8017 118.113 21.1019 118.113C17.4022 118.113 13.9491 117.125 10.9894 115.397C10.4961 115.15 10.1261 114.903 9.63281 114.533C9.63281 114.41 9.63281 114.286 9.63281 114.163C9.87946 112.188 10.9894 110.089 12.7159 108.732C12.9626 108.485 13.3325 108.361 13.7025 108.238C12.2226 107.991 11.236 107.374 10.6194 106.51H15.3057C16.6623 108.361 18.7588 109.472 21.2253 109.472C23.3218 109.472 25.1716 108.608 26.5282 107.25C26.6515 107.25 26.7748 107.25 26.8982 107.25C27.1448 107.25 27.3915 107.25 27.7614 107.25C28.748 107.25 29.7346 107.497 30.5979 107.991C32.3244 108.855 33.5577 110.336 34.4209 112.064C34.6676 112.311 34.6676 112.558 34.7909 112.805Z" fill="#525252"/> <path d="M34.7909 112.805C33.681 113.792 32.5711 114.656 31.2145 115.397C28.2547 117.125 24.8017 118.113 21.1019 118.113C17.4022 118.113 13.9491 117.125 10.9894 115.397C10.4961 115.15 10.1261 114.903 9.63281 114.533C9.63281 114.41 9.63281 114.286 9.63281 114.163C9.87946 112.188 10.9894 110.089 12.7159 108.732C12.9626 108.485 13.3325 108.361 13.7025 108.238C12.2226 107.991 11.236 107.374 10.6194 106.51H15.3057C16.6623 108.361 18.7588 109.472 21.2253 109.472C23.3218 109.472 25.1716 108.608 26.5282 107.25C26.6515 107.25 26.7748 107.25 26.8982 107.25C27.1448 107.25 27.3915 107.25 27.7614 107.25C28.748 107.25 29.7346 107.497 30.5979 107.991C32.3244 108.855 33.5577 110.336 34.4209 112.064C34.6676 112.311 34.6676 112.558 34.7909 112.805Z" fill="#575757"/>
<path d="M25.2953 104.165V106.757L17.5259 107.004L17.8958 105.275C18.0192 105.275 18.0192 105.399 18.1425 105.399C18.3891 105.522 18.6358 105.769 18.8824 105.893C19.0058 106.016 19.1291 106.139 19.3757 106.263C19.3757 106.263 19.4991 106.263 19.4991 106.386V104.658L19.3757 104.535C20.7323 105.275 22.5822 105.769 25.2953 104.165Z" fill="url(#paint2_linear)"/> <path d="M25.2953 104.165V106.757L17.5259 107.004L17.8958 105.275C18.0192 105.275 18.0192 105.399 18.1425 105.399C18.3891 105.522 18.6358 105.769 18.8824 105.893C19.0058 106.016 19.1291 106.139 19.3757 106.263C19.3757 106.263 19.4991 106.263 19.4991 106.386V104.658L19.3757 104.535C20.7323 105.275 22.5822 105.769 25.2953 104.165Z" fill="url(#paint2_linear_592_1141)"/>
<path d="M30.351 93.4261C28.8711 93.9198 27.1446 94.1667 25.5414 94.0432C22.9516 93.7964 20.4851 92.8089 18.5119 91.0808C17.8953 92.9323 16.2921 94.2901 14.4422 95.1541C14.1956 95.2776 13.949 95.401 13.7023 95.401C13.7023 95.401 13.7023 95.401 13.7023 95.2776C13.579 93.9198 13.579 92.562 14.0723 91.2042C14.4422 89.8465 15.3055 88.7356 16.2921 87.7481L16.4154 87.6247C16.5388 87.5012 16.7854 87.3778 16.9087 87.1309C17.772 86.3903 18.8819 85.8966 19.9918 85.6497C20.6084 85.5263 21.2251 85.4028 21.8417 85.4028C25.0481 85.4028 27.8846 87.1309 29.2411 89.8465C29.7344 90.8339 29.9811 91.8214 30.1044 92.562C30.351 93.0558 30.351 93.3026 30.351 93.4261Z" fill="#737373"/> <path d="M30.351 93.4261C28.8711 93.9198 27.1446 94.1667 25.5414 94.0432C22.9516 93.7964 20.4851 92.8089 18.5119 91.0808C17.8953 92.9323 16.2921 94.2901 14.4422 95.1541C14.1956 95.2776 13.949 95.401 13.7023 95.401C13.7023 95.401 13.7023 95.401 13.7023 95.2776C13.579 93.9198 13.579 92.562 14.0723 91.2042C14.4422 89.8465 15.3055 88.7356 16.2921 87.7481L16.4154 87.6247C16.5388 87.5012 16.7854 87.3778 16.9087 87.1309C17.772 86.3903 18.8819 85.8966 19.9918 85.6497C20.6084 85.5263 21.2251 85.4028 21.8417 85.4028C25.0481 85.4028 27.8846 87.1309 29.2411 89.8465C29.7344 90.8339 29.9811 91.8214 30.1044 92.562C30.351 93.0558 30.351 93.3026 30.351 93.4261Z" fill="#545454"/>
<path d="M20.4853 111.694C19.7453 112.558 18.5121 112.558 17.4022 112.558C18.5121 111.447 17.8955 107.868 13.9491 108.238C8.52286 107.251 9.01616 101.573 14.4424 99.8445L15.3057 99.5977L15.429 99.7211C15.799 100.832 16.4156 101.819 17.0322 102.56C14.8124 104.412 17.8955 104.905 19.3754 106.387C20.6086 107.127 21.7185 110.213 20.4853 111.694Z" fill="#737373"/> <path d="M20.4853 111.694C19.7453 112.558 18.5121 112.558 17.4022 112.558C18.5121 111.447 17.8955 107.868 13.9491 108.238C8.52286 107.251 9.01616 101.573 14.4424 99.8445L15.3057 99.5977L15.429 99.7211C15.799 100.832 16.4156 101.819 17.0322 102.56C14.8124 104.412 17.8955 104.905 19.3754 106.387C20.6086 107.127 21.7185 110.213 20.4853 111.694Z" fill="#545454"/>
<defs> <defs>
<linearGradient id="paint0_linear" x1="58.2299" y1="30.8211" x2="58.2299" y2="28.0409" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_592_1141" x1="58.2301" y1="30.8211" x2="58.2301" y2="28.0409" gradientUnits="userSpaceOnUse">
<stop stop-color="#181818"/> <stop stop-color="#151515"/>
<stop offset="0.9913" stop-color="#222427"/> <stop offset="0.9913" stop-color="#0B0B0B"/>
</linearGradient> </linearGradient>
<linearGradient id="paint1_linear" x1="95.163" y1="107.023" x2="95.163" y2="104.243" gradientUnits="userSpaceOnUse"> <linearGradient id="paint1_linear_592_1141" x1="95.163" y1="107.023" x2="95.163" y2="104.243" gradientUnits="userSpaceOnUse">
<stop stop-color="#181818"/> <stop stop-color="#151515"/>
<stop offset="0.9913" stop-color="#222427"/> <stop offset="0.9913" stop-color="#0B0B0B"/>
</linearGradient> </linearGradient>
<linearGradient id="paint2_linear" x1="21.3956" y1="106.915" x2="21.3956" y2="105.428" gradientUnits="userSpaceOnUse"> <linearGradient id="paint2_linear_592_1141" x1="21.3956" y1="106.915" x2="21.3956" y2="105.428" gradientUnits="userSpaceOnUse">
<stop stop-color="#181818"/> <stop stop-color="#151515"/>
<stop offset="0.9913" stop-color="#222427"/> <stop offset="0.9913" stop-color="#0B0B0B"/>
</linearGradient> </linearGradient>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,24 +1,24 @@
<svg width="145" height="110" viewBox="0 0 145 110" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="145" height="110" viewBox="0 0 145 110" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M74.6528 108.28C104.218 108.28 128.187 84.3113 128.187 54.6402C128.187 24.9692 104.113 1 74.6528 1C45.0873 1 21.1182 24.9692 21.1182 54.6402C21.1182 84.3113 45.0873 108.28 74.6528 108.28Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M74.6528 108.28C104.218 108.28 128.187 84.3113 128.187 54.6402C128.187 24.9692 104.113 1 74.6528 1C45.0873 1 21.1182 24.9692 21.1182 54.6402C21.1182 84.3113 45.0873 108.28 74.6528 108.28Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M134.984 37.645C137.374 37.645 139.313 35.7068 139.313 33.3158C139.313 30.9248 137.374 28.9866 134.984 28.9866C132.593 28.9866 130.654 30.9248 130.654 33.3158C130.654 35.7068 132.593 37.645 134.984 37.645Z" fill="#525252"/> <path d="M134.984 37.645C137.374 37.645 139.313 35.7068 139.313 33.3158C139.313 30.9248 137.374 28.9866 134.984 28.9866C132.593 28.9866 130.654 30.9248 130.654 33.3158C130.654 35.7068 132.593 37.645 134.984 37.645Z" fill="#575757"/>
<path d="M141.319 20.7505C142.952 20.7505 144.275 19.4268 144.275 17.7939C144.275 16.1611 142.952 14.8374 141.319 14.8374C139.686 14.8374 138.362 16.1611 138.362 17.7939C138.362 19.4268 139.686 20.7505 141.319 20.7505Z" fill="#525252"/> <path d="M141.319 20.7505C142.952 20.7505 144.275 19.4268 144.275 17.7939C144.275 16.1611 142.952 14.8374 141.319 14.8374C139.686 14.8374 138.362 16.1611 138.362 17.7939C138.362 19.4268 139.686 20.7505 141.319 20.7505Z" fill="#575757"/>
<path d="M23.5469 19.4783C25.1797 19.4783 26.5034 18.1546 26.5034 16.5217C26.5034 14.8889 25.1797 13.5652 23.5469 13.5652C21.914 13.5652 20.5903 14.8889 20.5903 16.5217C20.5903 18.1546 21.914 19.4783 23.5469 19.4783Z" fill="#181818"/> <path d="M23.5466 19.4783C25.1795 19.4783 26.5032 18.1546 26.5032 16.5217C26.5032 14.8889 25.1795 13.5652 23.5466 13.5652C21.9138 13.5652 20.5901 14.8889 20.5901 16.5217C20.5901 18.1546 21.9138 19.4783 23.5466 19.4783Z" fill="#0E0E0E"/>
<path d="M5.49073 76.4976C8.52318 76.4976 10.9815 74.0393 10.9815 71.0068C10.9815 67.9744 8.52318 65.5161 5.49073 65.5161C2.45828 65.5161 0 67.9744 0 71.0068C0 74.0393 2.45828 76.4976 5.49073 76.4976Z" fill="#181818"/> <path d="M5.49073 76.4976C8.52318 76.4976 10.9815 74.0393 10.9815 71.0068C10.9815 67.9744 8.52318 65.5161 5.49073 65.5161C2.45828 65.5161 0 67.9744 0 71.0068C0 74.0393 2.45828 76.4976 5.49073 76.4976Z" fill="#0E0E0E"/>
<path d="M85.5262 69.1928V89.5045C85.5262 91.2707 84.4985 93.037 83.0304 93.9201L67.4679 102.898C66.587 103.487 65.4125 103.782 64.3848 103.782V78.4656L84.7921 66.6907C85.2326 67.4266 85.5262 68.3097 85.5262 69.1928Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M85.5262 69.1928V89.5045C85.5262 91.2707 84.4985 93.037 83.0304 93.9201L67.4679 102.898C66.587 103.487 65.4125 103.782 64.3848 103.782V78.4656L84.7921 66.6907C85.2326 67.4266 85.5262 68.3097 85.5262 69.1928Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M64.3846 78.4656V103.782C63.3569 103.782 62.3292 103.487 61.3015 102.898L45.739 93.9201C44.1241 93.037 43.2432 91.4179 43.2432 89.5045V69.1928C43.2432 68.3097 43.5368 67.4266 43.9772 66.6907L64.3846 78.4656Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M64.3846 78.4656V103.782C63.3569 103.782 62.3292 103.487 61.3015 102.898L45.739 93.9201C44.1241 93.037 43.2432 91.4179 43.2432 89.5045V69.1928C43.2432 68.3097 43.5368 67.4266 43.9772 66.6907L64.3846 78.4656Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M84.7921 66.6908L64.3848 78.4657L43.8306 66.5437C44.271 65.8077 44.8583 65.0718 45.7392 64.6302L61.7421 55.3575C63.2102 54.4744 65.1188 54.4744 66.7338 55.2103L83.1772 64.7774C83.1772 64.7774 83.1772 64.7774 83.324 64.7774C83.9112 65.3662 84.4985 65.9549 84.7921 66.6908Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M84.7921 66.6908L64.3848 78.4657L43.8306 66.5437C44.271 65.8077 44.8583 65.0718 45.7392 64.6302L61.7421 55.3575C63.2102 54.4744 65.1188 54.4744 66.7338 55.2103L83.1772 64.7774C83.1772 64.7774 83.1772 64.7774 83.324 64.7774C83.9112 65.3662 84.4985 65.9549 84.7921 66.6908Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M127.809 69.1928V89.5045C127.809 91.2707 126.782 93.037 125.314 93.9201L109.751 102.898C108.87 103.487 107.696 103.782 106.668 103.782V78.4656L127.075 66.6907C127.516 67.4266 127.809 68.3097 127.809 69.1928Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M127.809 69.1928V89.5045C127.809 91.2707 126.781 93.037 125.313 93.9201L109.751 102.898C108.87 103.487 107.695 103.782 106.668 103.782V78.4656L127.075 66.6907C127.515 67.4266 127.809 68.3097 127.809 69.1928Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M106.668 78.4656V103.782C105.64 103.782 104.612 103.487 103.585 102.898L88.0222 93.9201C86.4073 93.037 85.5264 91.4179 85.5264 89.5045V69.1928C85.5264 68.3097 85.82 67.4266 86.2604 66.6907L106.668 78.4656Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M106.668 78.4656V103.782C105.64 103.782 104.612 103.487 103.584 102.898L88.022 93.9201C86.407 93.037 85.5261 91.4179 85.5261 89.5045V69.1928C85.5261 68.3097 85.8198 67.4266 86.2602 66.6907L106.668 78.4656Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M127.075 66.6908L106.668 78.4657L86.1138 66.5437C86.5542 65.8077 87.1415 65.0718 88.0224 64.6302L104.025 55.3575C105.493 54.4744 107.402 54.4744 109.017 55.2103L125.46 64.7774C125.46 64.7774 125.46 64.7774 125.607 64.7774C126.194 65.3662 126.782 65.9549 127.075 66.6908Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M127.075 66.6908L106.667 78.4657L86.1133 66.5437C86.5537 65.8077 87.141 65.0718 88.0219 64.6302L104.025 55.3575C105.493 54.4744 107.402 54.4744 109.017 55.2103L125.46 64.7774C125.46 64.7774 125.46 64.7774 125.607 64.7774C126.194 65.3662 126.781 65.9549 127.075 66.6908Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M64.2381 30.7776V51.0892C64.2381 52.8554 63.2104 54.6217 61.7423 55.5048L46.1798 64.4831C45.2989 65.0719 44.1244 65.3662 43.0967 65.3662V40.0503L63.5041 28.2754C64.0913 29.0113 64.2381 29.8944 64.2381 30.7776Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M64.2381 30.7776V51.0892C64.2381 52.8554 63.2104 54.6217 61.7423 55.5048L46.1798 64.4831C45.2989 65.0719 44.1244 65.3662 43.0967 65.3662V40.0503L63.5041 28.2754C64.0913 29.0113 64.2381 29.8944 64.2381 30.7776Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M43.0965 40.0503V65.3662C42.0688 65.3662 41.0411 65.0719 40.0134 64.4831L24.4509 55.5048C22.836 54.6217 21.9551 53.0026 21.9551 51.0892V30.7776C21.9551 29.8944 22.2487 29.0113 22.6892 28.2754L43.0965 40.0503Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M43.0965 40.0503V65.3662C42.0688 65.3662 41.0411 65.0719 40.0134 64.4831L24.4509 55.5048C22.836 54.6217 21.9551 53.0026 21.9551 51.0892V30.7776C21.9551 29.8944 22.2487 29.0113 22.6892 28.2754L43.0965 40.0503Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M63.6505 28.2751L43.2432 40.0499L22.689 28.1279C23.1294 27.392 23.7167 26.656 24.5976 26.2145L40.6005 16.9418C42.0686 16.0586 43.9772 16.0586 45.5922 16.7946L61.8887 26.5088C61.8887 26.5088 61.8887 26.5088 62.0356 26.5088C62.6228 26.9504 63.2101 27.5391 63.6505 28.2751Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M63.6505 28.2751L43.2432 40.0499L22.689 28.1279C23.1294 27.392 23.7167 26.656 24.5976 26.2145L40.6005 16.9418C42.0686 16.0586 43.9772 16.0586 45.5922 16.7946L61.8887 26.5088C61.8887 26.5088 61.8887 26.5088 62.0356 26.5088C62.6228 26.9504 63.2101 27.5391 63.6505 28.2751Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M109.311 30.3358V53.0024C109.311 55.063 108.283 56.9765 106.374 58.0068L88.9031 68.1626C87.8754 68.7513 86.7009 69.0457 85.5264 69.0457V40.6388L108.576 27.3921C109.017 28.2752 109.311 29.3055 109.311 30.3358Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M109.31 30.3358V53.0024C109.31 55.063 108.283 56.9765 106.374 58.0068L88.9029 68.1626C87.8752 68.7513 86.7006 69.0457 85.5261 69.0457V40.6388L108.576 27.3921C109.017 28.2752 109.31 29.3055 109.31 30.3358Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M85.5262 40.6388V58.5955V69.0457C84.3516 69.0457 83.1771 68.7513 82.1494 68.1626L74.515 63.747L64.5315 58.0067C62.7697 56.9764 61.742 55.063 61.5952 53.1496V30.1886C61.5952 29.1583 61.8888 28.128 62.3293 27.2449L85.5262 40.6388Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M85.5262 40.6388V58.5955V69.0457C84.3516 69.0457 83.1771 68.7513 82.1494 68.1626L74.515 63.747L64.5315 58.0067C62.7697 56.9764 61.742 55.063 61.5952 53.1496V30.1886C61.5952 29.1583 61.8888 28.128 62.3293 27.2449L85.5262 40.6388Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M108.429 27.3921L85.3792 40.6388L62.3291 27.2449C62.7695 26.3618 63.5036 25.6258 64.3845 25.1843L82.296 14.7341C84.0578 13.7038 86.1132 13.7038 87.875 14.5869L106.374 25.3315C106.374 25.3315 106.521 25.3315 106.521 25.4787C107.402 26.0674 107.989 26.6561 108.429 27.3921Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M108.429 27.3921L85.3792 40.6388L62.3291 27.2449C62.7695 26.3618 63.5036 25.6258 64.3845 25.1843L82.296 14.7341C84.0578 13.7038 86.1132 13.7038 87.875 14.5869L106.374 25.3315C106.374 25.3315 106.521 25.3315 106.521 25.4787C107.402 26.0674 107.989 26.6561 108.429 27.3921Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M108.206 12.7656C108.856 12.7656 109.383 13.2922 109.383 13.9418V20.9987C109.383 21.6483 108.856 22.1749 108.206 22.1749C107.557 22.1749 107.03 21.6483 107.03 20.9987V13.9418C107.03 13.2922 107.557 12.7656 108.206 12.7656Z" fill="#737373"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M108.207 12.7656C108.856 12.7656 109.383 13.2922 109.383 13.9418V20.9987C109.383 21.6483 108.856 22.1749 108.207 22.1749C107.557 22.1749 107.03 21.6483 107.03 20.9987V13.9418C107.03 13.2922 107.557 12.7656 108.207 12.7656Z" fill="#545454"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M123.936 13.1101C124.396 13.5694 124.396 14.3141 123.936 14.7735L115.311 23.3986C114.852 23.8579 114.107 23.8579 113.648 23.3986C113.188 22.9393 113.188 22.1946 113.648 21.7353L122.273 13.1101C122.732 12.6508 123.477 12.6508 123.936 13.1101Z" fill="#737373"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M123.936 13.1101C124.396 13.5694 124.396 14.3141 123.936 14.7735L115.311 23.3986C114.852 23.8579 114.107 23.8579 113.648 23.3986C113.188 22.9393 113.188 22.1946 113.648 21.7353L122.273 13.1101C122.732 12.6508 123.477 12.6508 123.936 13.1101Z" fill="#545454"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M114.087 29.6239C114.087 28.9743 114.614 28.4478 115.264 28.4478H123.105C123.754 28.4478 124.281 28.9743 124.281 29.6239C124.281 30.2735 123.754 30.8001 123.105 30.8001H115.264C114.614 30.8001 114.087 30.2735 114.087 29.6239Z" fill="#737373"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M114.087 29.6239C114.087 28.9743 114.614 28.4478 115.264 28.4478H123.105C123.754 28.4478 124.281 28.9743 124.281 29.6239C124.281 30.2735 123.754 30.8001 123.105 30.8001H115.264C114.614 30.8001 114.087 30.2735 114.087 29.6239Z" fill="#545454"/>
<path d="M96.9656 43.7842C96.7253 44.8023 96.3648 45.8804 95.9443 46.7788C94.8027 48.9948 93.0003 50.7317 90.7772 51.8696C88.4942 53.0076 85.7905 53.4867 83.0868 52.8878C76.7182 51.5702 72.6326 45.3414 73.9544 38.9928C75.2762 32.6442 81.4646 28.5116 87.8333 29.8891C90.1164 30.3683 92.099 31.5062 93.7813 33.0634C96.6052 35.8784 97.8068 39.9511 96.9656 43.7842Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M96.9654 43.7842C96.7251 44.8023 96.3646 45.8804 95.944 46.7788C94.8025 48.9948 93 50.7317 90.777 51.8696C88.4939 53.0076 85.7902 53.4867 83.0866 52.8878C76.7179 51.5702 72.6324 45.3414 73.9542 38.9928C75.276 32.6442 81.4644 28.5116 87.833 29.8891C90.1161 30.3683 92.0988 31.5062 93.7811 33.0634C96.6049 35.8784 97.8065 39.9511 96.9654 43.7842Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M89.1596 40.3732H86.4797V37.6933C86.4797 37.1573 86.0628 36.6809 85.4673 36.6809C84.9314 36.6809 84.4549 37.0978 84.4549 37.6933V40.3732H81.7751C81.2391 40.3732 80.7627 40.79 80.7627 41.3855C80.7627 41.9811 81.1796 42.3979 81.7751 42.3979H84.4549V45.0778C84.4549 45.6137 84.8718 46.0902 85.4673 46.0902C86.0033 46.0902 86.4797 45.6733 86.4797 45.0778V42.3979H89.1596C89.6955 42.3979 90.172 41.9811 90.172 41.3855C90.172 40.79 89.6955 40.3732 89.1596 40.3732Z" fill="#525252"/> <path d="M89.1597 40.3732H86.4798V37.6933C86.4798 37.1573 86.063 36.6809 85.4674 36.6809C84.9315 36.6809 84.4551 37.0978 84.4551 37.6933V40.3732H81.7752C81.2392 40.3732 80.7628 40.79 80.7628 41.3855C80.7628 41.9811 81.1797 42.3979 81.7752 42.3979H84.4551V45.0778C84.4551 45.6137 84.8719 46.0902 85.4674 46.0902C86.0034 46.0902 86.4798 45.6733 86.4798 45.0778V42.3979H89.1597C89.6957 42.3979 90.1721 41.9811 90.1721 41.3855C90.1721 40.79 89.6957 40.3732 89.1597 40.3732Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -1,38 +0,0 @@
<svg width="123" height="126" viewBox="0 0 123 126" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M64.4237 111.867C91.7974 111.867 113.99 89.6745 113.99 62.2031C113.99 34.7317 91.6996 12.5396 64.4237 12.5396C37.0501 12.5396 14.8579 34.7317 14.8579 62.2031C14.8579 89.6745 37.0501 111.867 64.4237 111.867Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/>
<path d="M93.6338 12.5396C83.2422 33.022 67.9889 41.1703 54.0085 46.0363C50.764 47.1812 47.2331 48.183 43.8931 47.3243C40.5531 46.4656 37.6425 43.0785 38.5491 39.7392C39.3602 36.6383 43.3682 35.1117 46.2788 36.3521C49.2371 37.5924 50.8594 41.0272 50.5731 44.2235C50.2868 47.4197 48.3305 50.2343 45.8494 52.1903C43.3205 54.1462 40.3145 55.3388 37.2608 56.2452C30.533 58.2488 23.519 59.2029 16.9821 61.7313C10.3976 64.2597 4.14697 68.7917 1.9044 75.4227C0.282106 80.1456 0.950108 85.4886 3.04954 89.9729C5.14898 94.4572 8.72756 98.2259 12.7356 101.231C18.8907 105.811 26.4773 108.673 34.1116 108.483C41.7459 108.292 49.4757 104.952 54.1994 98.9415C58.9231 92.9306 60.3068 84.2005 56.9668 77.2833C54.6765 72.5127 49.4757 68.6009 44.2748 69.7458C40.1714 70.6999 37.1653 74.7072 36.7359 78.9529C36.3065 83.1987 38.1673 87.3968 40.9348 90.5453C43.7022 93.7416 47.3762 95.9837 51.1934 97.9397C56.776 100.754 63.3606 102.901 69.134 100.516C74.6212 98.2736 77.818 92.5967 79.9175 87.1106C82.0169 81.6244 83.5915 75.6613 87.4086 71.1293C91.9892 65.6909 99.7189 63.1625 106.638 64.7845C113.556 66.4064 119.377 72.1311 121.095 79.0006C122.431 84.3436 120.809 91.0224 115.799 93.36C111.886 95.2205 107.067 93.8847 103.679 91.2132C100.292 88.5417 98.0012 84.7253 95.8064 81.0043" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4 4"/>
<path d="M55.1784 35.3718C63.5321 38.1997 72.5966 33.7202 75.4245 25.3665C78.2524 17.0128 73.7729 7.94831 65.4192 5.1204C57.0655 2.29248 48.001 6.77201 45.1731 15.1257C42.3451 23.4794 46.8247 32.5439 55.1784 35.3718Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.1375 32.5372C62.9259 34.8352 70.2919 31.1951 72.5899 24.4067C74.8879 17.6184 71.2478 10.2524 64.4594 7.95438C57.671 5.65637 50.3051 9.29651 48.007 16.0849C45.709 22.8732 49.3492 30.2392 56.1375 32.5372Z" fill="#181818"/>
<path d="M59.7561 21.8493C60.6415 22.149 61.6023 21.6742 61.902 20.7888C62.2018 19.9033 61.7269 18.9426 60.8415 18.6428C59.9561 18.3431 58.9953 18.8179 58.6956 19.7033C58.3958 20.5888 58.8706 21.5495 59.7561 21.8493Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M63.0122 12.23L60.6604 19.1773" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M62.083 20.2544L66.1857 20.4519" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M64.0744 10.7699C64.3349 10.6457 64.433 10.3214 64.2934 10.0454C64.1538 9.76951 63.8294 9.64651 63.5689 9.77072C63.3084 9.89492 63.2103 10.2193 63.3499 10.4952C63.4895 10.7711 63.8139 10.8941 64.0744 10.7699Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M57.3166 30.7045C57.5771 30.5803 57.6752 30.2559 57.5356 29.98C57.396 29.7041 57.0716 29.5811 56.8111 29.7053C56.5506 29.8295 56.4525 30.1539 56.5921 30.4298C56.7317 30.7057 57.0561 30.8287 57.3166 30.7045Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M50.7199 17.3822C50.9804 17.258 51.0785 16.9337 50.9389 16.6577C50.7993 16.3818 50.4749 16.2588 50.2144 16.383C49.9539 16.5072 49.8558 16.8316 49.9954 17.1075C50.135 17.3834 50.4594 17.5064 50.7199 17.3822Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M70.6545 24.14C70.915 24.0158 71.0131 23.6915 70.8735 23.4155C70.7339 23.1396 70.4095 23.0166 70.149 23.1408C69.8884 23.265 69.7904 23.5894 69.93 23.8653C70.0696 24.1412 70.394 24.2642 70.6545 24.14Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M12.3005 40.0978L14.9425 39.3015C15.1339 39.246 15.1339 38.9682 14.9425 38.9126L12.3005 38.1163C12.2326 38.0978 12.1832 38.0484 12.1647 37.9805L11.3684 35.3447C11.3128 35.1533 11.0351 35.1533 10.9795 35.3447L10.1832 37.9805C10.1647 38.0484 10.1153 38.0978 10.0474 38.1163L7.41159 38.9126C7.22023 38.9682 7.22023 39.246 7.41159 39.3015L10.0536 40.0978C10.1215 40.1163 10.1709 40.1657 10.1894 40.2336L10.9857 42.8756C11.0412 43.067 11.319 43.067 11.3746 42.8756L12.1709 40.2336C12.1832 40.1657 12.2326 40.1163 12.3005 40.0978Z" fill="#3c3c3c"/>
<path d="M96.5557 9.17336L99.2576 9.73368C99.4523 9.77613 99.5848 9.53199 99.4431 9.39188L97.5008 7.4318C97.45 7.38314 97.4301 7.31618 97.4463 7.24767L98.0036 4.55119C98.0461 4.35651 97.8019 4.22401 97.6618 4.3657L95.7047 6.30252C95.656 6.35336 95.5891 6.37321 95.5206 6.3571L92.8241 5.79972C92.6294 5.75727 92.4969 6.00142 92.6386 6.14152L94.5808 8.1016C94.6317 8.15026 94.6515 8.21722 94.6354 8.28573L94.0751 10.9876C94.0326 11.1823 94.2768 11.3148 94.4169 11.1731L96.377 9.23089C96.4202 9.17709 96.4872 9.15725 96.5557 9.17336Z" fill="#3c3c3c"/>
<path d="M94.8952 76.0129L96.9595 74.1819C97.1099 74.0512 96.9933 73.799 96.7963 73.8289L94.064 74.2143C93.9946 74.226 93.9291 74.2019 93.8838 74.148L92.0553 72.0895C91.9246 71.9391 91.6724 72.0556 91.7023 72.2526L92.085 74.9792C92.0967 75.0486 92.0726 75.1142 92.0187 75.1594L89.96 76.9879C89.8096 77.1186 89.9262 77.3708 90.1232 77.3409L92.8555 76.9555C92.9249 76.9438 92.9904 76.9679 93.0357 77.0218L94.8668 79.086C94.9975 79.2363 95.2497 79.1198 95.2198 78.9228L94.8345 76.1906C94.8172 76.1238 94.8413 76.0582 94.8952 76.0129Z" fill="#3c3c3c"/>
<path d="M73.8716 57.3258L76.1834 56.6291C76.3508 56.5805 76.3508 56.3374 76.1834 56.2888L73.8716 55.5921C73.8122 55.5759 73.769 55.5327 73.7528 55.4733L73.0561 53.1671C73.0074 52.9996 72.7644 52.9996 72.7158 53.1671L72.019 55.4733C72.0028 55.5327 71.9596 55.5759 71.9002 55.5921L69.5938 56.2888C69.4264 56.3374 69.4264 56.5805 69.5938 56.6291L71.9056 57.3258C71.965 57.342 72.0082 57.3852 72.0244 57.4446L72.7212 59.7563C72.7698 59.9237 73.0128 59.9237 73.0615 59.7563L73.7582 57.4446C73.769 57.3852 73.8122 57.342 73.8716 57.3258Z" fill="#3c3c3c"/>
<path d="M29.5118 89.7614L31.8235 88.9651C31.991 88.9095 31.991 88.6317 31.8235 88.5762L29.5118 87.7799C29.4524 87.7614 29.4092 87.712 29.393 87.6441L28.6962 85.0083C28.6476 84.8169 28.4045 84.8169 28.3559 85.0083L27.6591 87.6441C27.6429 87.712 27.5997 87.7614 27.5403 87.7799L25.234 88.5762C25.0665 88.6317 25.0665 88.9095 25.234 88.9651L27.5457 89.7614C27.6051 89.7799 27.6483 89.8293 27.6646 89.8972L28.3613 92.5392C28.4099 92.7305 28.653 92.7305 28.7016 92.5392L29.3984 89.8972C29.4092 89.8293 29.4524 89.7799 29.5118 89.7614Z" fill="#3c3c3c"/>
<path d="M25.6922 50.6715L27.3434 50.1738C27.463 50.1391 27.463 49.9655 27.3434 49.9308L25.6922 49.4331C25.6497 49.4216 25.6188 49.3907 25.6073 49.3483L25.1096 47.701C25.0749 47.5814 24.9013 47.5814 24.8665 47.701L24.3688 49.3483C24.3573 49.3907 24.3264 49.4216 24.284 49.4331L22.6366 49.9308C22.517 49.9655 22.517 50.1391 22.6366 50.1738L24.2878 50.6715C24.3303 50.6831 24.3611 50.7139 24.3727 50.7564L24.8704 52.4075C24.9051 52.5271 25.0787 52.5271 25.1134 52.4075L25.6111 50.7564C25.6188 50.7139 25.6497 50.6831 25.6922 50.6715Z" fill="#3c3c3c"/>
<path d="M120.831 51.7623L122.482 51.2647C122.602 51.2299 122.602 51.0563 122.482 51.0216L120.831 50.524C120.788 50.5124 120.758 50.4815 120.746 50.4391L120.248 48.7918C120.214 48.6723 120.04 48.6723 120.005 48.7918L119.508 50.4391C119.496 50.4815 119.465 50.5124 119.423 50.524L117.775 51.0216C117.656 51.0563 117.656 51.2299 117.775 51.2647L119.426 51.7623C119.469 51.7739 119.5 51.8047 119.511 51.8472L120.009 53.4983C120.044 53.6179 120.217 53.6179 120.252 53.4983L120.75 51.8472C120.758 51.8047 120.788 51.7739 120.831 51.7623Z" fill="#3c3c3c"/>
<path d="M37.8972 27.6539L39.5485 27.1563C39.6681 27.1215 39.6681 26.9479 39.5485 26.9132L37.8972 26.4156C37.8548 26.404 37.8239 26.3731 37.8124 26.3307L37.3147 24.6834C37.2799 24.5639 37.1063 24.5639 37.0716 24.6834L36.5739 26.3307C36.5623 26.3731 36.5315 26.404 36.489 26.4156L34.8417 26.9132C34.7221 26.9479 34.7221 27.1215 34.8417 27.1563L36.4929 27.6539C36.5353 27.6655 36.5662 27.6963 36.5778 27.7388L37.0755 29.3899C37.1102 29.5095 37.2838 29.5095 37.3185 29.3899L37.8162 27.7388C37.8239 27.6963 37.8548 27.6655 37.8972 27.6539Z" fill="#3c3c3c"/>
<path d="M13.3069 83.1031L14.9581 82.6055C15.0777 82.5708 15.0777 82.3972 14.9581 82.3624L13.3069 81.8648C13.2645 81.8532 13.2336 81.8224 13.222 81.7799L12.7243 80.1327C12.6896 80.0131 12.516 80.0131 12.4813 80.1327L11.9836 81.7799C11.972 81.8224 11.9411 81.8532 11.8987 81.8648L10.2513 82.3624C10.1317 82.3972 10.1317 82.5708 10.2513 82.6055L11.9026 83.1031C11.945 83.1147 11.9759 83.1456 11.9874 83.188L12.4851 84.8391C12.5199 84.9587 12.6935 84.9587 12.7282 84.8391L13.2259 83.188C13.2336 83.1456 13.2645 83.1147 13.3069 83.1031Z" fill="#3c3c3c"/>
<path d="M58.6922 57.5907L61.979 62.276C62.9181 63.6146 63.5603 64.7335 64.0498 66.2448C65.8824 71.0942 65.1988 76.4246 62.6771 80.7618C62.209 81.5182 61.9707 82.3987 62.0863 83.1736C62.1063 84.0157 62.356 84.9818 62.8927 85.7468C63.9659 87.2767 65.7655 88.0116 67.5737 87.7419C73.3808 86.6643 79.3534 88.4667 83.4436 92.8737C83.9417 93.3803 84.3442 93.954 84.8137 94.6233L88.1005 99.3086C88.5029 99.8823 88.8098 100.523 89.1452 101.001L61.0335 120.722C60.6025 120.311 60.133 119.641 59.7976 119.163L56.5108 114.478C56.1754 114 55.9357 113.455 55.6003 112.977C55.4661 112.786 55.3605 112.432 55.1593 112.145C52.5704 106.827 52.88 100.76 55.8413 95.8296C56.7775 94.3168 56.6319 92.2789 55.5586 90.749C55.0891 90.0797 54.3612 89.4489 53.5763 89.1435C53.5092 89.0479 53.4136 89.1149 53.4136 89.1149C52.6287 88.8095 51.7867 88.8295 50.9447 88.8495C47.0414 89.5903 43.0411 88.9724 39.4319 87.0814C38.5128 86.5848 37.5937 86.0882 36.7988 85.3618C36.569 85.2376 36.3678 84.9507 36.2336 84.7595C35.3716 83.9375 34.4425 83.0198 33.7047 81.968L30.4179 77.2828C30.3508 77.1872 30.2167 76.9959 30.0825 76.8047L58.3854 56.9499C58.491 57.3038 58.5581 57.3995 58.6922 57.5907Z" fill="#181818" stroke="#3c3c3c" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M74.5356 102.547C74.5356 102.547 64.4216 111.925 63.7422 111.974C63.2927 112.146 59.3138 113.796 56.7792 114.861L56.1085 113.905C55.7731 113.426 55.4377 112.948 55.1309 112.308C55.255 112.078 55.3121 111.752 55.3692 111.427C56.6664 107.378 60.698 101.126 69.1952 101.443L74.5356 102.547Z" fill="#181818" stroke="#3c3c3c" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.6922 57.5907L61.979 62.276C62.9181 63.6146 63.5603 64.7335 64.0498 66.2448C65.8824 71.0942 65.1988 76.4246 62.6771 80.7618C62.209 81.5182 61.9707 82.3987 62.0863 83.1736C62.1063 84.0157 62.356 84.9818 62.8927 85.7468C63.9659 87.2767 65.7655 88.0116 67.5737 87.7419C73.3808 86.6643 79.3534 88.4667 83.4436 92.8737C83.9417 93.3803 84.3442 93.954 84.8137 94.6233L88.1005 99.3086C88.5029 99.8823 88.8098 100.523 89.1452 101.001L61.0335 120.722C60.6025 120.311 60.133 119.641 59.7976 119.163L56.5108 114.478C56.1754 114 55.9357 113.455 55.6003 112.977C55.4661 112.786 55.3605 112.432 55.1593 112.145C52.5704 106.827 52.88 100.76 55.8413 95.8296C56.7775 94.3168 56.6319 92.2789 55.5586 90.749C55.0891 90.0797 54.3612 89.4489 53.5763 89.1435C53.5092 89.0479 53.4136 89.1149 53.4136 89.1149C52.6287 88.8095 51.7867 88.8295 50.9447 88.8495C47.0414 89.5903 43.0411 88.9724 39.4319 87.0814C38.5128 86.5848 37.5937 86.0882 36.7988 85.3618C36.569 85.2376 36.3678 84.9507 36.2336 84.7595C35.3716 83.9375 34.4425 83.0198 33.7047 81.968L30.4179 77.2828C30.3508 77.1872 30.2167 76.9959 30.0825 76.8047L58.3854 56.9499C58.491 57.3038 58.5581 57.3995 58.6922 57.5907Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.5304 77.084L45.3631 85.774L39.5946 87.1099C38.6755 86.6132 37.7565 86.1166 36.9615 85.3902C36.7318 85.266 36.5305 84.9792 36.3964 84.788C39.4818 79.6274 42.3203 76.352 50.5763 74.6979C51.9635 74.4381 53.2551 74.2454 54.805 74.0142C68.6011 72.3258 56.5304 77.084 56.5304 77.084Z" fill="#181818" stroke="#525252" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M64.1171 66.3406C65.9496 71.19 65.266 76.5204 62.7443 80.8576C62.2762 81.6139 62.0379 82.4945 62.1535 83.2694L62.0579 83.3365C60.528 84.4097 60.1284 86.6875 61.2017 88.2174L71.1961 102.464L68.4232 104.41L58.3617 90.067C57.2885 88.5371 55.1063 88.0704 53.4808 89.2107C52.6959 88.9053 51.8539 88.9253 51.0119 88.9453C47.1086 89.6861 43.1084 89.0682 39.4991 87.1772C38.58 86.6806 37.6609 86.184 36.866 85.4576C45.3803 83.7649 48.2459 78.9011 50.4523 74.9279C51.1687 73.712 51.7895 72.5631 52.4773 71.5099C55.2003 67.4596 59.2177 66.0681 64.1171 66.3406Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.7178 94.6907L88.0046 99.376C88.407 99.9497 88.7139 100.59 89.0493 101.069L63.9974 118.643L59.3949 118.59L56.4435 114.383C56.1081 113.905 55.7727 113.427 55.5329 112.881C55.3988 112.69 55.2932 112.336 55.0919 112.049C55.1876 111.982 55.2546 112.078 55.3503 112.011C55.513 112.039 55.6086 111.972 55.7042 111.905C65.022 109.934 67.5137 104.334 69.8727 99.9684C70.2452 99.2791 70.7133 98.5227 71.1528 97.929C74.0385 93.9073 78.2186 92.5443 83.2807 92.8454C83.7502 93.5147 84.2483 94.0214 84.7178 94.6907Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.9889 57.4062L29.6263 79.4074C28.957 79.8769 27.9808 79.7057 27.5783 79.132L25.4319 76.0722C24.9623 75.4029 25.2007 74.5223 25.87 74.0528L57.3282 51.9845C57.9976 51.515 58.9067 51.5906 59.3762 52.2599L61.4556 55.2241C61.7339 56.0275 61.6583 56.9366 60.9889 57.4062Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M92.6489 102.538L61.2863 124.539C60.6169 125.009 59.7078 124.933 59.2383 124.264L57.1589 121.3C56.6894 120.63 56.9277 119.75 57.597 119.28L89.0553 97.212C89.7246 96.7425 90.6337 96.8181 91.1032 97.4875L93.1826 100.452C93.4609 101.255 93.3182 102.069 92.6489 102.538Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.308 35.6053L109.875 38.2699C109.466 39.0312 109.09 39.6002 108.492 40.1902C106.636 42.2069 103.956 43.1493 101.309 42.9878C100.841 42.9462 100.368 43.0427 100.03 43.2813C99.6367 43.4906 99.2396 43.8379 99.0057 44.2729C98.5378 45.143 98.6179 46.1675 99.1745 46.9576C101.062 49.4447 101.628 52.6936 100.518 55.6717C100.397 56.0272 100.221 56.3535 100.017 56.7342L98.5837 59.3988C98.4083 59.7251 98.1785 60.0221 98.0323 60.294L82.0444 51.6971C82.1362 51.396 82.3409 51.0153 82.4871 50.7434L83.9199 48.0788C84.0661 47.8069 84.2667 47.5642 84.4129 47.2923C84.4714 47.1836 84.6135 47.0497 84.7012 46.8865C86.5983 44.4014 89.5374 43.1075 92.5693 43.3358C93.5061 43.4189 94.4341 42.8663 94.902 41.9962C95.1067 41.6156 95.2319 41.122 95.1898 40.6788C95.219 40.6244 95.1646 40.5952 95.1646 40.5952C95.1226 40.1519 94.9132 39.759 94.7039 39.366C93.4275 37.6982 92.7698 35.662 92.8062 33.5084C92.8226 32.9563 92.839 32.4043 92.9934 31.8564C92.9975 31.7184 93.0852 31.5553 93.1437 31.4465C93.3274 30.8442 93.5402 30.1876 93.8619 29.5894L95.2947 26.9247C95.324 26.8704 95.3824 26.7616 95.4409 26.6528L111.538 35.3082C111.395 35.4421 111.366 35.4965 111.308 35.6053Z" fill="#181818" stroke="#3c3c3c" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M93.8341 53.7603C93.8341 53.7603 87.0037 51.2092 86.8196 50.8998C86.6313 50.7285 84.9075 49.2407 83.803 48.2963L84.0954 47.7525C84.2416 47.4806 84.3878 47.2087 84.6176 46.9116C84.7556 46.9157 84.9229 46.8655 85.0901 46.8152C87.3104 46.4668 91.2207 46.8869 93.0881 50.9756L93.8341 53.7603Z" fill="#181818" stroke="#3c3c3c" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.308 35.6053L109.875 38.2699C109.466 39.0312 109.09 39.6002 108.492 40.1902C106.636 42.2069 103.956 43.1493 101.309 42.9878C100.841 42.9462 100.368 43.0427 100.03 43.2813C99.6367 43.4906 99.2396 43.8379 99.0057 44.2729C98.5378 45.143 98.6179 46.1675 99.1745 46.9576C101.062 49.4447 101.628 52.6936 100.518 55.6717C100.397 56.0272 100.221 56.3535 100.017 56.7342L98.5837 59.3988C98.4083 59.7251 98.1785 60.0221 98.0323 60.294L82.0444 51.6971C82.1362 51.396 82.3409 51.0153 82.4871 50.7434L83.9199 48.0788C84.0661 47.8069 84.2667 47.5642 84.4129 47.2923C84.4714 47.1836 84.6135 47.0497 84.7012 46.8865C86.5983 44.4014 89.5374 43.1075 92.5693 43.3358C93.5061 43.4189 94.4341 42.8663 94.902 41.9962C95.1067 41.6156 95.2319 41.122 95.1898 40.6788C95.219 40.6244 95.1646 40.5952 95.1646 40.5952C95.1226 40.1519 94.9132 39.759 94.7039 39.366C93.4275 37.6982 92.7698 35.662 92.8062 33.5084C92.8226 32.9563 92.839 32.4043 92.9934 31.8564C92.9975 31.7184 93.0852 31.5553 93.1437 31.4465C93.3274 30.8442 93.5402 30.1876 93.8619 29.5894L95.2947 26.9247C95.324 26.8704 95.3824 26.7616 95.4409 26.6528L111.538 35.3082C111.395 35.4421 111.366 35.4965 111.308 35.6053Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.587 39.2113L94.8318 35.9994L92.8315 33.5918C92.8479 33.0398 92.8643 32.4878 93.0187 31.9399C93.0228 31.8019 93.1105 31.6388 93.169 31.53C96.339 31.7623 98.5598 32.3256 101.301 35.8325C101.753 36.4261 102.151 36.9904 102.628 37.6676C106.7 43.7832 101.587 39.2113 101.587 39.2113Z" fill="#181818" stroke="#525252" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M108.462 40.2444C106.607 42.2611 103.927 43.2036 101.28 43.042C100.811 43.0005 100.339 43.0969 100 43.3355L99.9459 43.3063C99.0758 42.8384 97.9051 43.1904 97.4372 44.0605L93.0803 52.1632L91.5033 51.3152L95.8894 43.1581C96.3573 42.288 96.0597 41.1465 95.1353 40.6494C95.0932 40.2062 94.8839 39.8132 94.6745 39.4202C93.3981 37.7524 92.7404 35.7163 92.7768 33.5626C92.7932 33.0106 92.8096 32.4586 92.964 31.9107C95.7847 35.5305 98.7622 35.7295 101.163 35.8285C101.907 35.8782 102.597 35.8987 103.258 35.9736C105.817 36.2983 107.428 37.8656 108.462 40.2444Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.9622 56.7048L98.5294 59.3694C98.354 59.6957 98.1241 59.9928 97.9779 60.2647L83.7302 52.6035L82.6626 50.417L83.9492 48.0243C84.0954 47.7524 84.2416 47.4805 84.4422 47.2378C84.5007 47.129 84.6428 46.9951 84.7305 46.832C84.7849 46.8612 84.7556 46.9156 84.81 46.9449C84.8352 47.0285 84.8895 47.0577 84.9439 47.087C88.0867 51.0202 91.3234 50.8678 93.9454 50.9457C94.3594 50.958 94.8278 50.9996 95.2126 51.0663C97.7972 51.4746 99.4332 53.1255 100.493 55.5879C100.288 55.9686 100.167 56.3241 99.9622 56.7048Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.939 36.6459L94.1026 27.0548C93.722 26.8502 93.5711 26.3484 93.7466 26.0221L94.6823 24.282C94.887 23.9013 95.3595 23.8048 95.7402 24.0095L113.631 33.6299C114.012 33.8346 114.192 34.2819 113.987 34.6626L113.081 36.3484C112.767 36.6706 112.32 36.8506 111.939 36.6459Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M98.1382 62.3137L80.3013 52.7226C79.9207 52.5179 79.7406 52.0706 79.9453 51.6899L80.8517 50.0041C81.0564 49.6235 81.5289 49.527 81.9096 49.7317L99.8008 59.352C100.181 59.5567 100.362 60.0041 100.157 60.3847L99.2504 62.0705C98.9369 62.3927 98.5188 62.5184 98.1382 62.3137Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,22 +1,22 @@
<svg width="144" height="118" viewBox="0 0 144 118" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="144" height="117" viewBox="0 0 144 117" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M71.915 116.003C103.558 116.003 129.168 90.2405 129.168 58.455C129.168 26.6694 103.558 1 71.915 1C40.3653 1 14.6616 26.7621 14.6616 58.5476C14.6616 90.3332 40.3653 116.003 71.915 116.003Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M71.9152 116.003C103.558 116.003 129.169 90.2405 129.169 58.455C129.169 26.6694 103.558 1 71.9152 1C40.3655 1 14.6617 26.7621 14.6617 58.5476C14.6617 90.3332 40.3655 116.003 71.9152 116.003Z" fill="#0E0E0E" stroke="#3D3D3D" stroke-width="2" stroke-miterlimit="10"/>
<path d="M6.40267 71.9221C8.53691 71.9221 10.2072 70.2541 10.2072 68.1227C10.2072 65.9913 8.53691 64.3232 6.40267 64.3232C4.26842 64.3232 2.59814 65.9913 2.59814 68.1227C2.59814 70.2541 4.36122 71.9221 6.40267 71.9221Z" fill="#181818"/> <path d="M6.40267 71.9221C8.53691 71.9221 10.2072 70.2541 10.2072 68.1227C10.2072 65.9913 8.53691 64.3232 6.40267 64.3232C4.26842 64.3232 2.59814 65.9913 2.59814 68.1227C2.59814 70.2541 4.36122 71.9221 6.40267 71.9221Z" fill="#0E0E0E"/>
<path d="M2.59821 59.5972C3.99011 59.5972 5.19642 58.3925 5.19642 57.0025C5.19642 55.6124 3.99011 54.4077 2.59821 54.4077C1.20631 54.4077 0 55.6124 0 57.0025C0 58.4852 1.20631 59.5972 2.59821 59.5972Z" fill="#181818"/> <path d="M2.59821 59.5972C3.99011 59.5972 5.19642 58.3925 5.19642 57.0025C5.19642 55.6124 3.99011 54.4077 2.59821 54.4077C1.20631 54.4077 0 55.6124 0 57.0025C0 58.4852 1.20631 59.5972 2.59821 59.5972Z" fill="#0E0E0E"/>
<path d="M138.726 81.467C141.417 81.467 143.551 79.3356 143.551 76.6481C143.551 73.9607 141.417 71.8293 138.726 71.8293C136.035 71.8293 133.9 73.9607 133.9 76.6481C133.9 79.3356 136.035 81.467 138.726 81.467Z" fill="#181818"/> <path d="M138.726 81.467C141.417 81.467 143.551 79.3356 143.551 76.6481C143.551 73.9607 141.417 71.8293 138.726 71.8293C136.035 71.8293 133.901 73.9607 133.901 76.6481C133.901 79.3356 136.035 81.467 138.726 81.467Z" fill="#0E0E0E"/>
<circle cx="70.6176" cy="28.6049" r="2.23823" fill="#4D4D4D"/> <circle cx="70.6175" cy="28.6049" r="2.23823" fill="#4D4D4D"/>
<path d="M113.372 106.197H29.3551C26.224 106.197 23.6147 103.61 23.6147 100.505V15.6447C23.6147 12.5401 26.224 9.95288 29.3551 9.95288H113.372C116.503 9.95288 119.113 12.5401 119.113 15.6447V100.505C119.113 103.61 116.503 106.197 113.372 106.197Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M113.372 106.197H29.3551C26.224 106.197 23.6147 103.61 23.6147 100.505V15.6447C23.6147 12.5401 26.224 9.95288 29.3551 9.95288H113.372C116.503 9.95288 119.113 12.5401 119.113 15.6447V100.505C119.113 103.61 116.503 106.197 113.372 106.197Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M70.8772 42.0341C77.4699 42.0341 82.8144 36.6896 82.8144 30.0969C82.8144 23.5041 77.4699 18.1597 70.8772 18.1597C64.2844 18.1597 58.9399 23.5041 58.9399 30.0969C58.9399 36.6896 64.2844 42.0341 70.8772 42.0341Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M70.8772 42.0341C77.4699 42.0341 82.8144 36.6896 82.8144 30.0969C82.8144 23.5041 77.4699 18.1597 70.8772 18.1597C64.2844 18.1597 58.9399 23.5041 58.9399 30.0969C58.9399 36.6896 64.2844 42.0341 70.8772 42.0341Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M70.8773 30.8431C73.3496 30.8431 75.3538 28.8389 75.3538 26.3666C75.3538 23.8943 73.3496 21.8901 70.8773 21.8901C68.4051 21.8901 66.4009 23.8943 66.4009 26.3666C66.4009 28.8389 68.4051 30.8431 70.8773 30.8431Z" fill="#525252"/> <path d="M70.8772 30.8431C73.3495 30.8431 75.3537 28.8389 75.3537 26.3666C75.3537 23.8943 73.3495 21.8901 70.8772 21.8901C68.4049 21.8901 66.4008 23.8943 66.4008 26.3666C66.4008 28.8389 68.4049 30.8431 70.8772 30.8431Z" fill="#575757"/>
<path d="M79.8302 38.3074C77.6656 40.6044 74.6218 42.0342 71.2503 42.0342C67.8788 42.0342 64.8349 40.6044 62.6704 38.3074C63.8656 34.8375 67.2553 32.3352 71.2503 32.3352C75.2452 32.3352 78.635 34.8375 79.8302 38.3074Z" fill="#525252"/> <path d="M79.8302 38.3074C77.6656 40.6044 74.6218 42.0342 71.2503 42.0342C67.8788 42.0342 64.8349 40.6044 62.6704 38.3074C63.8656 34.8375 67.2553 32.3352 71.2503 32.3352C75.2452 32.3352 78.635 34.8375 79.8302 38.3074Z" fill="#575757"/>
<path d="M103.388 60.686H38.5928C37.8184 60.686 37.0439 59.8252 37.0439 58.9643V51.2166C37.0439 50.3557 37.8184 49.4949 38.5928 49.4949H103.388C104.162 49.4949 104.937 50.3557 104.937 51.2166V58.9643C104.937 59.8252 104.162 60.686 103.388 60.686Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M103.388 60.686H38.593C37.8185 60.686 37.0441 59.8252 37.0441 58.9643V51.2166C37.0441 50.3557 37.8185 49.4949 38.593 49.4949H103.388C104.163 49.4949 104.937 50.3557 104.937 51.2166V58.9643C104.937 59.8252 104.163 60.686 103.388 60.686Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M103.388 77.0998H38.5928C37.8184 77.0998 37.0439 76.239 37.0439 75.3781V67.6304C37.0439 66.7695 37.8184 65.9087 38.5928 65.9087H103.388C104.162 65.9087 104.937 66.7695 104.937 67.6304V75.3781C104.937 76.239 104.162 77.0998 103.388 77.0998Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M103.388 77.0998H38.593C37.8185 77.0998 37.0441 76.239 37.0441 75.3781V67.6304C37.0441 66.7695 37.8185 65.9087 38.593 65.9087H103.388C104.163 65.9087 104.937 66.7695 104.937 67.6304V75.3781C104.937 76.239 104.163 77.0998 103.388 77.0998Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M103.325 83.8145H79.6896C78.8839 83.8145 78.0781 84.4877 78.0781 85.8342V93.2398C78.0781 94.2497 78.6153 95.2595 79.6896 95.2595H103.325C104.131 95.2595 104.937 94.5863 104.937 93.2398V85.8342C104.937 84.7682 104.131 83.8145 103.325 83.8145Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M103.325 83.8145H79.6898C78.884 83.8145 78.0782 84.4877 78.0782 85.8342V93.2398C78.0782 94.2497 78.6154 95.2595 79.6898 95.2595H103.325C104.131 95.2595 104.937 94.5863 104.937 93.2398V85.8342C104.937 84.7682 104.131 83.8145 103.325 83.8145Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M97.9375 88.291H85.8239C85.3084 88.291 84.793 88.9141 84.793 89.5371C84.793 90.1601 85.3084 90.7832 85.8239 90.7832H97.9375C98.453 90.7832 98.9684 90.1601 98.9684 89.5371C98.9684 88.6025 98.453 88.291 97.9375 88.291Z" fill="#737373"/> <path d="M97.9375 88.291H85.8239C85.3084 88.291 84.793 88.9141 84.793 89.5371C84.793 90.1601 85.3084 90.7832 85.8239 90.7832H97.9375C98.453 90.7832 98.9684 90.1601 98.9684 89.5371C98.9684 88.6025 98.453 88.291 97.9375 88.291Z" fill="#545454"/>
<rect x="41.5205" y="53.9714" width="29.097" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="41.5205" y="53.9714" width="29.097" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="41.5205" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="41.5205" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="45.9971" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="45.9971" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="50.4736" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="50.4734" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="54.9497" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="54.9498" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="58.6802" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="58.6803" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
<rect x="63.1567" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/> <rect x="63.1567" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -1,37 +1,38 @@
<svg width="145" height="120" viewBox="0 0 145 120" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="145" height="120" viewBox="0 0 145 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M130.648 59.7265C130.648 72.9542 126.219 85.3837 118.837 95.1904C114.748 100.55 109.865 105.225 104.187 108.874C95.1015 114.918 84.0856 118.339 72.3883 118.339C40.2491 118.453 14.1289 92.2256 14.1289 59.7265C14.1289 27.3414 40.1355 1 72.3883 1C84.0856 1 94.9879 4.42096 104.187 10.4647C109.865 14.1137 114.748 18.789 118.837 24.1485C126.219 34.0693 130.648 46.3847 130.648 59.7265Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/> <path d="M130.648 59.7265C130.648 72.9542 126.219 85.3837 118.837 95.1904C114.748 100.55 109.865 105.225 104.187 108.874C95.1015 114.918 84.0856 118.339 72.3883 118.339C40.2491 118.453 14.1289 92.2256 14.1289 59.7265C14.1289 27.3414 40.1355 1 72.3883 1C84.0856 1 94.9879 4.42096 104.187 10.4647C109.865 14.1137 114.748 18.789 118.837 24.1485C126.219 34.0693 130.648 46.3847 130.648 59.7265Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M28.0195 51.8743H27.3162C26.7887 51.8743 26.437 51.7102 26.437 51.464C26.437 51.2178 26.7887 51.0537 27.3162 51.0537H28.0195C28.547 51.0537 28.8987 51.2178 28.8987 51.464C28.8987 51.7102 28.547 51.8743 28.0195 51.8743Z" fill="#2D5887"/> <path d="M28.0198 51.8743H27.3164C26.7889 51.8743 26.4373 51.7102 26.4373 51.464C26.4373 51.2178 26.7889 51.0537 27.3164 51.0537H28.0198C28.5473 51.0537 28.8989 51.2178 28.8989 51.464C28.8989 51.7102 28.5473 51.8743 28.0198 51.8743Z" fill="#7F7F7F"/>
<path d="M28.0195 55.1565H27.3162C26.7887 55.1565 26.437 54.9924 26.437 54.7462C26.437 54.5 26.7887 54.3359 27.3162 54.3359H28.0195C28.547 54.3359 28.8987 54.5 28.8987 54.7462C28.8987 54.9924 28.547 55.1565 28.0195 55.1565Z" fill="#2D5887"/> <path d="M28.0198 55.1565H27.3164C26.7889 55.1565 26.4373 54.9924 26.4373 54.7462C26.4373 54.5 26.7889 54.3359 27.3164 54.3359H28.0198C28.5473 54.3359 28.8989 54.5 28.8989 54.7462C28.8989 54.9924 28.5473 55.1565 28.0198 55.1565Z" fill="#7F7F7F"/>
<path d="M28.0195 58.4387H27.3162C26.7887 58.4387 26.437 58.2746 26.437 58.0284C26.437 57.7823 26.7887 57.6182 27.3162 57.6182H28.0195C28.547 57.6182 28.8987 57.7823 28.8987 58.0284C28.8987 58.2746 28.547 58.4387 28.0195 58.4387Z" fill="#2D5887"/> <path d="M28.0198 58.4387H27.3164C26.7889 58.4387 26.4373 58.2746 26.4373 58.0284C26.4373 57.7823 26.7889 57.6182 27.3164 57.6182H28.0198C28.5473 57.6182 28.8989 57.7823 28.8989 58.0284C28.8989 58.2746 28.5473 58.4387 28.0198 58.4387Z" fill="#7F7F7F"/>
<path d="M28.0195 61.7209H27.3162C26.7887 61.7209 26.437 61.5568 26.437 61.3107C26.437 61.0645 26.7887 60.9004 27.3162 60.9004H28.0195C28.547 60.9004 28.8987 61.0645 28.8987 61.3107C28.8987 61.5568 28.547 61.7209 28.0195 61.7209Z" fill="#2D5887"/> <path d="M28.0198 61.7209H27.3164C26.7889 61.7209 26.4373 61.5568 26.4373 61.3107C26.4373 61.0645 26.7889 60.9004 27.3164 60.9004H28.0198C28.5473 60.9004 28.8989 61.0645 28.8989 61.3107C28.8989 61.5568 28.5473 61.7209 28.0198 61.7209Z" fill="#7F7F7F"/>
<path d="M60.1189 51.8743H48.553C48.0841 51.8743 47.7715 51.7102 47.7715 51.464C47.7715 51.2178 48.0841 51.0537 48.553 51.0537H60.1189C60.5878 51.0537 60.9004 51.2178 60.9004 51.464C60.9004 51.7102 60.5878 51.8743 60.1189 51.8743Z" fill="white"/> <path d="M60.1191 51.8743H48.5532C48.0843 51.8743 47.7717 51.7102 47.7717 51.464C47.7717 51.2178 48.0843 51.0537 48.5532 51.0537H60.1191C60.588 51.0537 60.9006 51.2178 60.9006 51.464C60.9006 51.7102 60.588 51.8743 60.1191 51.8743Z" fill="white"/>
<path d="M84.7621 61.7209H48.5264C48.0734 61.7209 47.7715 61.5568 47.7715 61.3107C47.7715 61.0645 48.0734 60.9004 48.5264 60.9004H84.7621C85.215 60.9004 85.517 61.0645 85.517 61.3107C85.517 61.5568 85.215 61.7209 84.7621 61.7209Z" fill="url(#paint0_linear)"/> <path d="M84.7623 61.7209H48.5266C48.0737 61.7209 47.7717 61.5568 47.7717 61.3107C47.7717 61.0645 48.0737 60.9004 48.5266 60.9004H84.7623C85.2153 60.9004 85.5172 61.0645 85.5172 61.3107C85.5172 61.5568 85.2153 61.7209 84.7623 61.7209Z" fill="url(#paint0_linear_592_1292)"/>
<path d="M60.1642 55.1565H44.4053C43.9635 55.1565 43.6689 54.9924 43.6689 54.7462C43.6689 54.5 43.9635 54.3359 44.4053 54.3359H60.1642C60.606 54.3359 60.9006 54.5 60.9006 54.7462C60.9006 54.9924 60.606 55.1565 60.1642 55.1565Z" fill="#2D5887"/> <path d="M60.1642 55.1565H44.4053C43.9635 55.1565 43.6689 54.9924 43.6689 54.7462C43.6689 54.5 43.9635 54.3359 44.4053 54.3359H60.1642C60.606 54.3359 60.9006 54.5 60.9006 54.7462C60.9006 54.9924 60.606 55.1565 60.1642 55.1565Z" fill="#7F7F7F"/>
<path d="M72.4741 58.4387H44.4038C43.9629 58.4387 43.6689 58.2746 43.6689 58.0284C43.6689 57.7823 43.9629 57.6182 44.4038 57.6182H72.4741C72.915 57.6182 73.2089 57.7823 73.2089 58.0284C73.0619 58.2746 72.768 58.4387 72.4741 58.4387Z" fill="#2D5887"/> <path d="M72.4741 58.4387H44.4038C43.9629 58.4387 43.6689 58.2746 43.6689 58.0284C43.6689 57.7823 43.9629 57.6182 44.4038 57.6182H72.4741C72.915 57.6182 73.2089 57.7823 73.2089 58.0284C73.0619 58.2746 72.768 58.4387 72.4741 58.4387Z" fill="#7F7F7F"/>
<path d="M84.7181 58.4387H74.0082C73.5287 58.4387 73.209 58.2746 73.209 58.0284C73.209 57.7823 73.5287 57.6182 74.0082 57.6182H84.7181C85.1976 57.6182 85.5173 57.7823 85.5173 58.0284C85.5173 58.2746 85.1976 58.4387 84.7181 58.4387Z" fill="white"/> <path d="M84.7178 58.4387H74.008C73.5284 58.4387 73.2087 58.2746 73.2087 58.0284C73.2087 57.7823 73.5284 57.6182 74.008 57.6182H84.7178C85.1974 57.6182 85.5171 57.7823 85.5171 58.0284C85.5171 58.2746 85.1974 58.4387 84.7178 58.4387Z" fill="white"/>
<path d="M91.3056 58.4387H87.1141C86.6484 58.4387 86.3379 58.2746 86.3379 58.0284C86.3379 57.7823 86.6484 57.6182 87.1141 57.6182H91.3056C91.7713 57.6182 92.0818 57.7823 92.0818 58.0284C92.0818 58.2746 91.7713 58.4387 91.3056 58.4387Z" fill="url(#paint1_linear)"/> <path d="M91.3053 58.4387H87.1138C86.6481 58.4387 86.3376 58.2746 86.3376 58.0284C86.3376 57.7823 86.6481 57.6182 87.1138 57.6182H91.3053C91.771 57.6182 92.0815 57.7823 92.0815 58.0284C92.0815 58.2746 91.771 58.4387 91.3053 58.4387Z" fill="url(#paint1_linear_592_1292)"/>
<path d="M1 103.569H138.853" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M1 103.569H138.853" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M97.8252 51.0537V103.569H23.3713C20.4342 103.569 18.2314 101.209 18.2314 98.4062V51.0537H97.8252Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M97.8255 51.0537V103.569H23.3715C20.4345 103.569 18.2317 101.209 18.2317 98.4062V51.0537H97.8255Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M127.366 51.0537V98.4062C127.366 101.356 124.917 103.569 122.009 103.569H97.8257V51.0537H127.366Z" fill="#DAE1ED"/> <path d="M127.365 51.0537V98.4062C127.365 101.356 124.916 103.569 122.008 103.569H97.8254V51.0537H127.365Z" fill="#EAEAEA"/>
<path d="M127.366 51.0537V98.4062C127.366 101.356 124.917 103.569 122.009 103.569H97.8257V51.0537H127.366Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/> <path d="M127.365 51.0537V98.4062C127.365 101.356 124.916 103.569 122.008 103.569H97.8254V51.0537H127.365Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M46.9512 51.0537L62.6074 26.437H142.956L126.857 51.0537H46.9512Z" fill="#C5CDDB"/> <path d="M46.9512 51.0537L62.6074 26.437H142.956L126.857 51.0537H46.9512Z" fill="#D6D6D6"/>
<path d="M46.9512 51.0537L62.6074 26.437H142.956L126.857 51.0537H46.9512Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/> <path d="M46.9512 51.0537L62.6074 26.437H142.956L126.857 51.0537H46.9512Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path opacity="0.3" d="M127.366 51.1975V78.9526H105.826C103.826 78.9526 102.441 77.6583 102.134 75.7888L97.8257 51.0537L127.366 51.1975Z" fill="url(#paint2_linear)"/> <path opacity="0.3" d="M127.365 51.1975V78.9526H105.826C103.826 78.9526 102.441 77.6583 102.133 75.7888L97.8254 51.0537L127.365 51.1975Z" fill="url(#paint2_linear_592_1292)"/>
<path d="M126.747 51.0537H97.8257L112.511 73.3119C113.56 74.7859 115.208 75.6704 116.857 75.6704H139.334C140.833 75.6704 141.882 73.9015 140.982 72.7223L126.747 51.0537Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M126.746 51.0537H97.8254L112.511 73.3119C113.56 74.7859 115.208 75.6704 116.856 75.6704H139.334C140.832 75.6704 141.881 73.9015 140.982 72.7223L126.746 51.0537Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M97.8254 51.0537L82.0597 26.437H1L17.3607 51.0537H97.8254Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M97.8254 51.0537L82.0597 26.437H1L17.3607 51.0537H97.8254Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M57.9543 81.4143H26.9218C26.1967 81.4143 25.6167 80.8673 25.6167 80.1835C25.6167 79.4997 26.1967 78.9526 26.9218 78.9526H57.9543C58.6794 78.9526 59.2594 79.4997 59.2594 80.1835C59.1144 80.8673 58.6794 81.4143 57.9543 81.4143Z" fill="#3c3c3c"/> <path d="M57.9543 81.4143H26.9218C26.1967 81.4143 25.6167 80.8673 25.6167 80.1835C25.6167 79.4997 26.1967 78.9526 26.9218 78.9526H57.9543C58.6794 78.9526 59.2594 79.4997 59.2594 80.1835C59.1144 80.8673 58.6794 81.4143 57.9543 81.4143Z" fill="#575757"/>
<path d="M57.9543 87.1582H26.9218C26.1967 87.1582 25.6167 86.7935 25.6167 86.3376C25.6167 85.8818 26.1967 85.5171 26.9218 85.5171H57.9543C58.6794 85.5171 59.2594 85.8818 59.2594 86.3376C59.1144 86.7935 58.6794 87.1582 57.9543 87.1582Z" fill="#3c3c3c"/> <path d="M57.9543 87.1582H26.9218C26.1967 87.1582 25.6167 86.7935 25.6167 86.3376C25.6167 85.8818 26.1967 85.5171 26.9218 85.5171H57.9543C58.6794 85.5171 59.2594 85.8818 59.2594 86.3376C59.1144 86.7935 58.6794 87.1582 57.9543 87.1582Z" fill="#575757"/>
<path d="M41.5228 93.7227H26.9422C26.2058 93.7227 25.6167 93.358 25.6167 92.9021C25.6167 92.4462 26.2058 92.0815 26.9422 92.0815H41.5228C42.2592 92.0815 42.8483 92.4462 42.8483 92.9021C42.7011 93.358 42.1119 93.7227 41.5228 93.7227Z" fill="#3c3c3c"/> <path d="M41.5228 93.7227H26.9422C26.2058 93.7227 25.6167 93.358 25.6167 92.9021C25.6167 92.4462 26.2058 92.0815 26.9422 92.0815H41.5228C42.2592 92.0815 42.8483 92.4462 42.8483 92.9021C42.7011 93.358 42.1119 93.7227 41.5228 93.7227Z" fill="#575757"/>
<defs> <defs>
<linearGradient id="paint0_linear" x1="47.941" y1="61.3273" x2="85.5608" y2="61.3273" gradientUnits="userSpaceOnUse"> <linearGradient id="paint0_linear_592_1292" x1="47.9413" y1="61.3273" x2="85.561" y2="61.3273" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF66A9"/> <stop stop-color="#FF66A9"/>
<stop offset="1" stop-color="#F53689"/> <stop offset="0.0001" stop-color="#AAAAAA"/>
<stop offset="1" stop-color="#3B3B3B"/>
</linearGradient> </linearGradient>
<linearGradient id="paint1_linear" x1="86.3762" y1="58.0033" x2="92.0807" y2="58.0033" gradientUnits="userSpaceOnUse"> <linearGradient id="paint1_linear_592_1292" x1="86.376" y1="58.0033" x2="92.0804" y2="58.0033" gradientUnits="userSpaceOnUse">
<stop stop-color="#83A6FF"/> <stop stop-color="white"/>
<stop offset="1" stop-color="#5A78FF"/> <stop offset="1" stop-color="#8D8D8D"/>
</linearGradient> </linearGradient>
<linearGradient id="paint2_linear" x1="112.602" y1="79.5252" x2="112.602" y2="54.1127" gradientUnits="userSpaceOnUse"> <linearGradient id="paint2_linear_592_1292" x1="112.602" y1="79.5252" x2="112.602" y2="54.1127" gradientUnits="userSpaceOnUse">
<stop offset="0.00289017" stop-opacity="0"/> <stop offset="0.00289017" stop-opacity="0"/>
<stop offset="1"/> <stop offset="1"/>
</linearGradient> </linearGradient>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@@ -0,0 +1,38 @@
<svg width="123" height="126" viewBox="0 0 123 126" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M64.4237 111.867C91.7974 111.867 113.99 89.6745 113.99 62.2031C113.99 34.7317 91.6996 12.5396 64.4237 12.5396C37.0501 12.5396 14.8579 34.7317 14.8579 62.2031C14.8579 89.6745 37.0501 111.867 64.4237 111.867Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M93.6338 12.5396C83.2422 33.022 67.9889 41.1703 54.0085 46.0363C50.764 47.1812 47.2331 48.183 43.8931 47.3243C40.5531 46.4656 37.6425 43.0785 38.5491 39.7392C39.3602 36.6383 43.3682 35.1117 46.2788 36.3521C49.2371 37.5924 50.8594 41.0272 50.5731 44.2235C50.2868 47.4197 48.3305 50.2343 45.8494 52.1903C43.3205 54.1462 40.3145 55.3388 37.2608 56.2452C30.533 58.2488 23.519 59.2029 16.9821 61.7313C10.3976 64.2597 4.14697 68.7917 1.9044 75.4227C0.282106 80.1456 0.950108 85.4886 3.04954 89.9729C5.14898 94.4572 8.72756 98.2259 12.7356 101.231C18.8907 105.811 26.4773 108.673 34.1116 108.483C41.7459 108.292 49.4757 104.952 54.1994 98.9415C58.9231 92.9306 60.3068 84.2005 56.9668 77.2833C54.6765 72.5127 49.4757 68.6009 44.2748 69.7458C40.1714 70.6999 37.1653 74.7072 36.7359 78.9529C36.3065 83.1987 38.1673 87.3968 40.9348 90.5453C43.7022 93.7416 47.3762 95.9837 51.1934 97.9397C56.776 100.754 63.3606 102.901 69.134 100.516C74.6212 98.2736 77.818 92.5967 79.9175 87.1106C82.0169 81.6244 83.5915 75.6613 87.4086 71.1293C91.9892 65.6909 99.7189 63.1625 106.638 64.7845C113.556 66.4064 119.377 72.1311 121.095 79.0006C122.431 84.3436 120.809 91.0224 115.799 93.36C111.886 95.2205 107.067 93.8847 103.679 91.2132C100.292 88.5417 98.0012 84.7253 95.8064 81.0043" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4 4"/>
<path d="M55.1786 35.3718C63.5323 38.1997 72.5968 33.7202 75.4247 25.3665C78.2527 17.0128 73.7731 7.94831 65.4194 5.1204C57.0657 2.29248 48.0012 6.77201 45.1733 15.1257C42.3454 23.4794 46.8249 32.5439 55.1786 35.3718Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.138 32.5372C62.9264 34.8352 70.2924 31.1951 72.5904 24.4067C74.8884 17.6184 71.2482 10.2524 64.4599 7.95438C57.6715 5.65637 50.3055 9.29651 48.0075 16.0849C45.7095 22.8732 49.3497 30.2392 56.138 32.5372Z" fill="#0E0E0E"/>
<path d="M59.7563 21.8493C60.6417 22.149 61.6025 21.6742 61.9023 20.7888C62.202 19.9033 61.7272 18.9426 60.8418 18.6428C59.9563 18.3431 58.9955 18.8179 58.6958 19.7033C58.3961 20.5888 58.8709 21.5495 59.7563 21.8493Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M63.0127 12.23L60.6609 19.1773" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M62.0832 20.2544L66.1859 20.4519" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M64.0749 10.7699C64.3354 10.6457 64.4335 10.3214 64.2939 10.0454C64.1543 9.76951 63.8299 9.64651 63.5694 9.77072C63.3089 9.89492 63.2108 10.2193 63.3504 10.4952C63.49 10.7711 63.8144 10.8941 64.0749 10.7699Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M57.3169 30.7045C57.5774 30.5803 57.6754 30.2559 57.5358 29.98C57.3962 29.7041 57.0719 29.5811 56.8113 29.7053C56.5508 29.8295 56.4528 30.1539 56.5924 30.4298C56.732 30.7057 57.0563 30.8287 57.3169 30.7045Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M50.7202 17.3822C50.9807 17.258 51.0787 16.9337 50.9391 16.6577C50.7995 16.3818 50.4752 16.2588 50.2146 16.383C49.9541 16.5072 49.8561 16.8316 49.9957 17.1075C50.1353 17.3834 50.4596 17.5064 50.7202 17.3822Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M70.655 24.14C70.9155 24.0158 71.0136 23.6915 70.874 23.4155C70.7344 23.1396 70.41 23.0166 70.1495 23.1408C69.8889 23.265 69.7909 23.5894 69.9305 23.8653C70.0701 24.1412 70.3945 24.2642 70.655 24.14Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M12.3005 40.0978L14.9425 39.3015C15.1339 39.246 15.1339 38.9682 14.9425 38.9126L12.3005 38.1163C12.2326 38.0978 12.1832 38.0484 12.1647 37.9805L11.3684 35.3447C11.3128 35.1533 11.0351 35.1533 10.9795 35.3447L10.1832 37.9805C10.1647 38.0484 10.1153 38.0978 10.0474 38.1163L7.41159 38.9126C7.22023 38.9682 7.22023 39.246 7.41159 39.3015L10.0536 40.0978C10.1215 40.1163 10.1709 40.1657 10.1894 40.2336L10.9857 42.8756C11.0412 43.067 11.319 43.067 11.3746 42.8756L12.1709 40.2336C12.1832 40.1657 12.2326 40.1163 12.3005 40.0978Z" fill="#575757"/>
<path d="M96.5554 9.17336L99.2573 9.73368C99.452 9.77613 99.5845 9.53199 99.4428 9.39188L97.5006 7.4318C97.4497 7.38314 97.4299 7.31618 97.446 7.24767L98.0034 4.55119C98.0458 4.35651 97.8017 4.22401 97.6616 4.3657L95.7045 6.30252C95.6558 6.35336 95.5888 6.37321 95.5203 6.3571L92.8238 5.79972C92.6292 5.75727 92.4967 6.00142 92.6383 6.14152L94.5806 8.1016C94.6314 8.15026 94.6513 8.21722 94.6352 8.28573L94.0748 10.9876C94.0324 11.1823 94.2765 11.3148 94.4167 11.1731L96.3767 9.23089C96.42 9.17709 96.4869 9.15725 96.5554 9.17336Z" fill="#575757"/>
<path d="M94.8952 76.013L96.9595 74.1819C97.1099 74.0512 96.9933 73.799 96.7963 73.8289L94.064 74.2143C93.9946 74.226 93.9291 74.2019 93.8838 74.148L92.0553 72.0895C91.9246 71.9391 91.6724 72.0556 91.7023 72.2526L92.085 74.9792C92.0967 75.0486 92.0726 75.1142 92.0187 75.1594L89.96 76.9879C89.8096 77.1186 89.9262 77.3708 90.1232 77.3409L92.8555 76.9555C92.9249 76.9438 92.9904 76.9679 93.0357 77.0218L94.8668 79.086C94.9975 79.2363 95.2497 79.1198 95.2198 78.9228L94.8345 76.1906C94.8172 76.1238 94.8413 76.0582 94.8952 76.013Z" fill="#575757"/>
<path d="M73.8716 57.3258L76.1834 56.6291C76.3508 56.5805 76.3508 56.3374 76.1834 56.2888L73.8716 55.5921C73.8122 55.5759 73.769 55.5327 73.7528 55.4733L73.0561 53.1671C73.0074 52.9996 72.7644 52.9996 72.7158 53.1671L72.019 55.4733C72.0028 55.5327 71.9596 55.5759 71.9002 55.5921L69.5938 56.2888C69.4264 56.3374 69.4264 56.5805 69.5938 56.6291L71.9056 57.3258C71.965 57.342 72.0082 57.3852 72.0244 57.4446L72.7212 59.7563C72.7698 59.9237 73.0128 59.9237 73.0615 59.7563L73.7582 57.4446C73.769 57.3852 73.8122 57.342 73.8716 57.3258Z" fill="#575757"/>
<path d="M29.5118 89.7614L31.8235 88.9651C31.991 88.9095 31.991 88.6317 31.8235 88.5762L29.5118 87.7799C29.4524 87.7614 29.4092 87.712 29.393 87.6441L28.6962 85.0083C28.6476 84.8169 28.4045 84.8169 28.3559 85.0083L27.6591 87.6441C27.6429 87.712 27.5997 87.7614 27.5403 87.7799L25.234 88.5762C25.0665 88.6317 25.0665 88.9095 25.234 88.9651L27.5457 89.7614C27.6051 89.7799 27.6483 89.8293 27.6646 89.8972L28.3613 92.5392C28.4099 92.7305 28.653 92.7305 28.7016 92.5392L29.3984 89.8972C29.4092 89.8293 29.4524 89.7799 29.5118 89.7614Z" fill="#575757"/>
<path d="M25.6924 50.6715L27.3436 50.1738C27.4632 50.1391 27.4632 49.9655 27.3436 49.9308L25.6924 49.4331C25.65 49.4216 25.6191 49.3907 25.6075 49.3483L25.1098 47.701C25.0751 47.5814 24.9015 47.5814 24.8668 47.701L24.3691 49.3483C24.3575 49.3907 24.3266 49.4216 24.2842 49.4331L22.6368 49.9308C22.5172 49.9655 22.5172 50.1391 22.6368 50.1738L24.2881 50.6715C24.3305 50.6831 24.3614 50.7139 24.3729 50.7564L24.8706 52.4075C24.9054 52.5271 25.079 52.5271 25.1137 52.4075L25.6114 50.7564C25.6191 50.7139 25.65 50.6831 25.6924 50.6715Z" fill="#575757"/>
<path d="M120.831 51.7623L122.482 51.2647C122.601 51.2299 122.601 51.0563 122.482 51.0216L120.831 50.524C120.788 50.5124 120.757 50.4815 120.746 50.4391L120.248 48.7918C120.213 48.6723 120.04 48.6723 120.005 48.7918L119.507 50.4391C119.496 50.4815 119.465 50.5124 119.422 50.524L117.775 51.0216C117.655 51.0563 117.655 51.2299 117.775 51.2647L119.426 51.7623C119.469 51.7739 119.5 51.8047 119.511 51.8472L120.009 53.4983C120.044 53.6179 120.217 53.6179 120.252 53.4983L120.75 51.8472C120.757 51.8047 120.788 51.7739 120.831 51.7623Z" fill="#575757"/>
<path d="M37.8975 27.6539L39.5487 27.1563C39.6683 27.1215 39.6683 26.9479 39.5487 26.9132L37.8975 26.4156C37.855 26.404 37.8242 26.3731 37.8126 26.3307L37.3149 24.6834C37.2802 24.5639 37.1066 24.5639 37.0719 24.6834L36.5742 26.3307C36.5626 26.3731 36.5317 26.404 36.4893 26.4156L34.8419 26.9132C34.7223 26.9479 34.7223 27.1215 34.8419 27.1563L36.4931 27.6539C36.5356 27.6655 36.5664 27.6963 36.578 27.7388L37.0757 29.3899C37.1104 29.5095 37.284 29.5095 37.3188 29.3899L37.8165 27.7388C37.8242 27.6963 37.855 27.6655 37.8975 27.6539Z" fill="#575757"/>
<path d="M13.3067 83.1031L14.9579 82.6055C15.0775 82.5708 15.0775 82.3972 14.9579 82.3624L13.3067 81.8648C13.2642 81.8532 13.2334 81.8224 13.2218 81.7799L12.7241 80.1327C12.6894 80.0131 12.5158 80.0131 12.481 80.1327L11.9833 81.7799C11.9718 81.8224 11.9409 81.8532 11.8985 81.8648L10.2511 82.3624C10.1315 82.3972 10.1315 82.5708 10.2511 82.6055L11.9023 83.1031C11.9448 83.1147 11.9756 83.1456 11.9872 83.188L12.4849 84.8391C12.5196 84.9587 12.6932 84.9587 12.7279 84.8391L13.2256 83.188C13.2334 83.1456 13.2642 83.1147 13.3067 83.1031Z" fill="#575757"/>
<path d="M58.692 57.5907L61.9788 62.276C62.9178 63.6146 63.5601 64.7335 64.0496 66.2448C65.8821 71.0942 65.1986 76.4246 62.6769 80.7618C62.2088 81.5181 61.9704 82.3987 62.0861 83.1736C62.106 84.0157 62.3558 84.9818 62.8924 85.7468C63.9657 87.2767 65.7653 88.0116 67.5735 87.7419C73.3805 86.6643 79.3531 88.4667 83.4434 92.8737C83.9415 93.3803 84.3439 93.954 84.8135 94.6233L88.1002 99.3086C88.5027 99.8823 88.8095 100.523 89.1449 101.001L61.0333 120.722C60.6023 120.311 60.1327 119.641 59.7973 119.163L56.5106 114.478C56.1752 114 55.9354 113.455 55.6 112.977C55.4659 112.786 55.3603 112.432 55.159 112.145C52.5701 106.827 52.8798 100.76 55.841 95.8296C56.7772 94.3168 56.6316 92.2789 55.5584 90.749C55.0888 90.0797 54.361 89.4489 53.5761 89.1435C53.509 89.0479 53.4134 89.1149 53.4134 89.1149C52.6284 88.8095 51.7864 88.8295 50.9444 88.8495C47.0412 89.5903 43.0409 88.9724 39.4316 87.0814C38.5125 86.5848 37.5934 86.0882 36.7985 85.3618C36.5687 85.2376 36.3675 84.9507 36.2334 84.7595C35.3714 83.9375 34.4423 83.0198 33.7044 81.968L30.4177 77.2828C30.3506 77.1872 30.2164 76.9959 30.0823 76.8047L58.3851 56.9499C58.4908 57.3038 58.5578 57.3995 58.692 57.5907Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M74.5354 102.547C74.5354 102.547 64.4213 111.925 63.742 111.974C63.2924 112.146 59.3136 113.796 56.779 114.861L56.1082 113.905C55.7728 113.426 55.4375 112.948 55.1306 112.308C55.2548 112.078 55.3118 111.752 55.3689 111.427C56.6662 107.378 60.6978 101.126 69.195 101.443L74.5354 102.547Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.692 57.5907L61.9788 62.276C62.9178 63.6146 63.5601 64.7335 64.0496 66.2448C65.8821 71.0942 65.1986 76.4246 62.6769 80.7618C62.2088 81.5181 61.9704 82.3987 62.0861 83.1736C62.106 84.0157 62.3558 84.9818 62.8924 85.7468C63.9657 87.2767 65.7653 88.0116 67.5735 87.7419C73.3805 86.6643 79.3531 88.4667 83.4434 92.8737C83.9415 93.3803 84.3439 93.954 84.8135 94.6233L88.1002 99.3086C88.5027 99.8823 88.8095 100.523 89.1449 101.001L61.0333 120.722C60.6023 120.311 60.1327 119.641 59.7973 119.163L56.5106 114.478C56.1752 114 55.9354 113.455 55.6 112.977C55.4659 112.786 55.3603 112.432 55.159 112.145C52.5701 106.827 52.8798 100.76 55.841 95.8296C56.7772 94.3168 56.6316 92.2789 55.5584 90.749C55.0888 90.0797 54.361 89.4489 53.5761 89.1435C53.509 89.0479 53.4134 89.1149 53.4134 89.1149C52.6284 88.8095 51.7864 88.8295 50.9444 88.8495C47.0412 89.5903 43.0409 88.9724 39.4316 87.0814C38.5125 86.5848 37.5934 86.0882 36.7985 85.3618C36.5687 85.2376 36.3675 84.9507 36.2334 84.7595C35.3714 83.9375 34.4423 83.0198 33.7044 81.968L30.4177 77.2828C30.3506 77.1872 30.2164 76.9959 30.0823 76.8047L58.3851 56.9499C58.4908 57.3038 58.5578 57.3995 58.692 57.5907Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M56.5304 77.084L45.3631 85.774L39.5946 87.1099C38.6755 86.6132 37.7565 86.1166 36.9615 85.3902C36.7318 85.266 36.5305 84.9792 36.3964 84.788C39.4818 79.6274 42.3203 76.352 50.5763 74.6979C51.9635 74.4381 53.2551 74.2454 54.805 74.0142C68.6011 72.3258 56.5304 77.084 56.5304 77.084Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M64.1168 66.3406C65.9493 71.19 65.2658 76.5204 62.7441 80.8576C62.276 81.6139 62.0377 82.4945 62.1533 83.2694L62.0577 83.3365C60.5278 84.4097 60.1282 86.6875 61.2014 88.2174L71.1959 102.464L68.423 104.41L58.3614 90.067C57.2882 88.5371 55.1061 88.0704 53.4806 89.2107C52.6957 88.9053 51.8536 88.9253 51.0116 88.9453C47.1084 89.6861 43.1081 89.0681 39.4988 87.1772C38.5798 86.6806 37.6607 86.184 36.8657 85.4576C45.3801 83.7649 48.2457 78.9011 50.452 74.9279C51.1684 73.712 51.7892 72.5631 52.4771 71.5099C55.2 67.4596 59.2174 66.0681 64.1168 66.3406Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M84.7176 94.6907L88.0043 99.376C88.4068 99.9497 88.7136 100.59 89.049 101.069L63.9972 118.643L59.3946 118.59L56.4432 114.383C56.1079 113.905 55.7725 113.427 55.5327 112.881C55.3985 112.69 55.2929 112.336 55.0917 112.049C55.1873 111.982 55.2544 112.078 55.35 112.011C55.5127 112.039 55.6083 111.972 55.7039 111.905C65.0217 109.934 67.5135 104.334 69.8725 99.9684C70.245 99.2791 70.7131 98.5227 71.1526 97.929C74.0382 93.9073 78.2183 92.5443 83.2804 92.8454C83.7499 93.5147 84.248 94.0214 84.7176 94.6907Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M60.9889 57.4062L29.6263 79.4074C28.957 79.8769 27.9808 79.7057 27.5783 79.132L25.4319 76.0722C24.9623 75.4029 25.2007 74.5223 25.87 74.0528L57.3282 51.9845C57.9976 51.515 58.9067 51.5906 59.3762 52.2599L61.4556 55.2241C61.7339 56.0275 61.6583 56.9366 60.9889 57.4062Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M92.6484 102.538L61.2858 124.539C60.6165 125.009 59.7074 124.933 59.2378 124.264L57.1584 121.3C56.6889 120.63 56.9272 119.75 57.5965 119.28L89.0548 97.212C89.7241 96.7425 90.6332 96.8181 91.1028 97.4875L93.1821 100.452C93.4604 101.255 93.3177 102.069 92.6484 102.538Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.307 35.6053L109.874 38.2699C109.465 39.0312 109.089 39.6002 108.491 40.1902C106.636 42.2069 103.956 43.1493 101.309 42.9878C100.84 42.9462 100.368 43.0427 100.029 43.2813C99.6362 43.4906 99.2391 43.8379 99.0052 44.2729C98.5373 45.143 98.6174 46.1675 99.174 46.9576C101.061 49.4447 101.628 52.6936 100.517 55.6717C100.396 56.0272 100.221 56.3535 100.016 56.7342L98.5832 59.3988C98.4078 59.7251 98.178 60.0221 98.0318 60.294L82.0439 51.6971C82.1357 51.396 82.3404 51.0153 82.4866 50.7434L83.9194 48.0788C84.0656 47.8069 84.2662 47.5642 84.4124 47.2923C84.4709 47.1836 84.613 47.0497 84.7007 46.8865C86.5978 44.4014 89.5369 43.1075 92.5688 43.3358C93.5056 43.4189 94.4337 42.8663 94.9015 41.9962C95.1062 41.6156 95.2314 41.122 95.1893 40.6788C95.2185 40.6244 95.1642 40.5952 95.1642 40.5952C95.1221 40.1519 94.9128 39.759 94.7034 39.366C93.427 37.6982 92.7693 35.662 92.8057 33.5084C92.8221 32.9563 92.8385 32.4043 92.9929 31.8564C92.997 31.7184 93.0847 31.5553 93.1432 31.4465C93.3269 30.8442 93.5398 30.1876 93.8614 29.5894L95.2942 26.9247C95.3235 26.8704 95.3819 26.7616 95.4404 26.6528L111.537 35.3082C111.395 35.4421 111.366 35.4965 111.307 35.6053Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M93.8336 53.7603C93.8336 53.7603 87.0032 51.2092 86.8191 50.8998C86.6308 50.7285 84.907 49.2407 83.8025 48.2963L84.0949 47.7525C84.2411 47.4806 84.3873 47.2087 84.6171 46.9116C84.7551 46.9157 84.9224 46.8655 85.0896 46.8152C87.31 46.4668 91.2202 46.8869 93.0877 50.9756L93.8336 53.7603Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.307 35.6053L109.874 38.2699C109.465 39.0312 109.089 39.6002 108.491 40.1902C106.636 42.2069 103.956 43.1493 101.309 42.9878C100.84 42.9462 100.368 43.0427 100.029 43.2813C99.6362 43.4906 99.2391 43.8379 99.0052 44.2729C98.5373 45.143 98.6174 46.1675 99.174 46.9576C101.061 49.4447 101.628 52.6936 100.517 55.6717C100.396 56.0272 100.221 56.3535 100.016 56.7342L98.5832 59.3988C98.4078 59.7251 98.178 60.0221 98.0318 60.294L82.0439 51.6971C82.1357 51.396 82.3404 51.0153 82.4866 50.7434L83.9194 48.0788C84.0656 47.8069 84.2662 47.5642 84.4124 47.2923C84.4709 47.1836 84.613 47.0497 84.7007 46.8865C86.5978 44.4014 89.5369 43.1075 92.5688 43.3358C93.5056 43.4189 94.4337 42.8663 94.9015 41.9962C95.1062 41.6156 95.2314 41.122 95.1893 40.6788C95.2185 40.6244 95.1642 40.5952 95.1642 40.5952C95.1221 40.1519 94.9128 39.759 94.7034 39.366C93.427 37.6982 92.7693 35.662 92.8057 33.5084C92.8221 32.9563 92.8385 32.4043 92.9929 31.8564C92.997 31.7184 93.0847 31.5553 93.1432 31.4465C93.3269 30.8442 93.5398 30.1876 93.8614 29.5894L95.2942 26.9247C95.3235 26.8704 95.3819 26.7616 95.4404 26.6528L111.537 35.3082C111.395 35.4421 111.366 35.4965 111.307 35.6053Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.587 39.2113L94.8313 35.9994L92.831 33.5918C92.8474 33.0398 92.8638 32.4878 93.0182 31.9399C93.0223 31.8019 93.1101 31.6388 93.1685 31.53C96.3385 31.7623 98.5594 32.3256 101.3 35.8325C101.752 36.4261 102.15 36.9904 102.627 37.6676C106.7 43.7832 101.587 39.2113 101.587 39.2113Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.9781" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M108.462 40.2444C106.607 42.2611 103.927 43.2036 101.279 43.042C100.811 43.0005 100.339 43.0969 100 43.3355L99.9456 43.3063C99.0756 42.8384 97.9049 43.1904 97.437 44.0605L93.0801 52.1632L91.503 51.3152L95.8892 43.1581C96.3571 42.288 96.0595 41.1465 95.135 40.6494C95.0929 40.2062 94.8836 39.8132 94.6743 39.4202C93.3979 37.7524 92.7402 35.7163 92.7765 33.5626C92.7929 33.0106 92.8094 32.4586 92.9638 31.9107C95.7844 35.5305 98.762 35.7295 101.162 35.8285C101.907 35.8782 102.597 35.8987 103.258 35.9736C105.817 36.2983 107.428 37.8656 108.462 40.2444Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.962 56.7048L98.5292 59.3694C98.3537 59.6957 98.1239 59.9928 97.9777 60.2647L83.73 52.6035L82.6623 50.417L83.9489 48.0243C84.0952 47.7524 84.2414 47.4805 84.4419 47.2378C84.5004 47.129 84.6425 46.9951 84.7303 46.832C84.7846 46.8612 84.7554 46.9156 84.8098 46.9449C84.8349 47.0285 84.8893 47.0577 84.9437 47.087C88.0865 51.0202 91.3231 50.8678 93.9452 50.9457C94.3592 50.958 94.8276 50.9996 95.2123 51.0663C97.7969 51.4746 99.433 53.1255 100.492 55.5879C100.288 55.9686 100.167 56.3241 99.962 56.7048Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M111.939 36.6459L94.1024 27.0548C93.7217 26.8502 93.5709 26.3484 93.7463 26.0221L94.6821 24.282C94.8868 23.9013 95.3592 23.8048 95.7399 24.0095L113.631 33.6299C114.012 33.8346 114.192 34.2819 113.987 34.6626L113.081 36.3484C112.767 36.6706 112.32 36.8506 111.939 36.6459Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M98.1377 62.3137L80.3008 52.7226C79.9202 52.5179 79.7401 52.0706 79.9448 51.6899L80.8513 50.0041C81.0559 49.6235 81.5284 49.527 81.9091 49.7317L99.8003 59.352C100.181 59.5567 100.361 60.0041 100.156 60.3847L99.2499 62.0705C98.9364 62.3927 98.5183 62.5184 98.1377 62.3137Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,22 @@
<svg width="132" height="119" viewBox="0 0 132 119" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M68.2952 117.6C96.2952 117.6 118.995 94.9 118.995 66.8C118.995 38.7 96.1952 16 68.2952 16C40.2952 16 17.5952 38.7 17.5952 66.8C17.5952 94.9 40.2952 117.6 68.2952 117.6Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M115.295 33.6C117.56 33.6 119.395 31.7644 119.395 29.5C119.395 27.2357 117.56 25.4 115.295 25.4C113.031 25.4 111.195 27.2357 111.195 29.5C111.195 31.7644 113.031 33.6 115.295 33.6Z" fill="#575757"/>
<path d="M121.295 17.6C122.842 17.6 124.095 16.3464 124.095 14.8C124.095 13.2536 122.842 12 121.295 12C119.749 12 118.495 13.2536 118.495 14.8C118.495 16.3464 119.749 17.6 121.295 17.6Z" fill="#575757"/>
<path d="M13.3952 40.6C14.9416 40.6 16.1952 39.3464 16.1952 37.8C16.1952 36.2536 14.9416 35 13.3952 35C11.8488 35 10.5952 36.2536 10.5952 37.8C10.5952 39.3464 11.8488 40.6 13.3952 40.6Z" fill="#575757"/>
<path d="M28.7952 118.4C31.6671 118.4 33.9952 116.072 33.9952 113.2C33.9952 110.328 31.6671 108 28.7952 108C25.9233 108 23.5952 110.328 23.5952 113.2C23.5952 116.072 25.9233 118.4 28.7952 118.4Z" fill="#575757"/>
<path d="M130.815 80.551C130.815 93.3948 120.428 103.782 107.473 103.782C107.25 103.782 105.351 103.782 91.3903 103.782C81.6736 103.782 66.1494 103.782 41.3552 103.782H29.5166C13.9923 104.117 1.59521 91.7195 1.59521 76.7537C1.59521 61.6762 14.104 49.1674 29.8516 49.9492C43.3655 7.73215 105.239 13.6515 110.488 57.3204C122.104 58.7724 130.815 68.6007 130.815 80.551Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M110.153 57.4321C109.26 57.3204 108.366 57.3204 107.473 57.3204C102.894 57.3204 98.5381 58.6607 94.8525 61.0061" fill="#0E0E0E"/>
<path d="M110.153 57.4321C109.26 57.3204 108.366 57.3204 107.473 57.3204C102.894 57.3204 98.5381 58.6607 94.8525 61.0061" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M29.8516 49.9491C28.5114 53.9698 27.8413 58.2138 27.8413 62.6812C27.8413 64.4682 27.953 66.1435 28.1764 67.8188" fill="#0E0E0E"/>
<path d="M29.8516 49.9491C28.5114 53.9698 27.8413 58.2138 27.8413 62.6812C27.8413 64.4682 27.953 66.1435 28.1764 67.8188" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M58.2129 63.5102L60.1676 61.551C60.7377 60.9796 60.7377 60 60.1676 59.4286C59.5975 58.8571 58.6201 58.8571 58.05 59.4286L56.0952 61.3878L54.1405 59.4286C53.5703 58.8571 52.593 58.8571 52.0228 59.4286C51.4527 60 51.4527 60.9796 52.0228 61.551L53.9776 63.5102L52.0228 65.4694C51.4527 66.0408 51.4527 67.0204 52.0228 67.5918C52.3486 67.9184 52.6744 68 53.0816 68C53.4889 68 53.8147 67.8367 54.1405 67.5918L56.0952 65.6327L58.05 67.5918C58.3758 67.9184 58.7016 68 59.1088 68C59.516 68 59.8418 67.8367 60.1676 67.5918C60.7377 67.0204 60.7377 66.0408 60.1676 65.4694L58.2129 63.5102Z" fill="#545454"/>
<path d="M78.9482 63.5102L81.1201 61.551C81.7536 60.9796 81.7536 60 81.1201 59.4286C80.4866 58.8571 79.4006 58.8571 78.7672 59.4286L76.5952 61.3878L74.4233 59.4286C73.7898 58.8571 72.7038 58.8571 72.0703 59.4286C71.4368 60 71.4368 60.9796 72.0703 61.551L74.2423 63.5102L72.0703 65.4694C71.4368 66.0408 71.4368 67.0204 72.0703 67.5918C72.4323 67.9184 72.7943 68 73.2468 68C73.6993 68 74.0613 67.8367 74.4233 67.5918L76.5952 65.6327L78.7672 67.5918C79.1291 67.9184 79.4911 68 79.9436 68C80.3961 68 80.7581 67.8367 81.1201 67.5918C81.7536 67.0204 81.7536 66.0408 81.1201 65.4694L78.9482 63.5102Z" fill="#545454"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.3315 24.0521C22.5659 23.13 23.5034 22.5725 24.4256 22.8068L30.9457 24.4642C31.8678 24.6986 32.4254 25.6361 32.191 26.5582C31.9566 27.4804 31.019 28.0379 30.0969 27.8035L23.5767 26.1461C22.6546 25.9117 22.0971 24.9742 22.3315 24.0521Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.8717 5.71515C28.6589 5.1808 29.7302 5.3858 30.2646 6.17302L38.059 17.656C38.5934 18.4432 38.3884 19.5146 37.6012 20.0489C36.8139 20.5833 35.7426 20.3783 35.2082 19.5911L27.4138 8.10806C26.8795 7.32084 27.0845 6.2495 27.8717 5.71515Z" fill="#575757"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.3303 0.536284C43.2636 0.351547 44.17 0.958405 44.3548 1.89174L47.0495 15.5061C47.2342 16.4394 46.6274 17.3458 45.694 17.5306C44.7607 17.7153 43.8543 17.1084 43.6696 16.1751L40.9749 2.56073C40.7901 1.6274 41.397 0.721022 42.3303 0.536284Z" fill="#575757"/>
<rect x="55.5952" y="77" width="23" height="3" rx="1.5" fill="#545454"/>
<path d="M69.5952 78H73.5952V83C73.5952 84.1046 72.6998 85 71.5952 85C70.4906 85 69.5952 84.1046 69.5952 83V78Z" fill="#545454"/>
<path d="M122.189 34.0647C121.763 35.8614 121.123 37.5889 120.342 39.1783C118.281 42.9098 115.225 45.9503 111.388 47.8161C107.408 49.82 102.718 50.511 97.9573 49.6127C86.8716 47.3323 79.6944 36.8288 82.0394 25.9798C84.3845 15.1308 95.1148 8.15147 106.271 10.4318C110.251 11.2611 113.733 13.1268 116.646 15.8218C121.479 20.6589 123.539 27.5 122.189 34.0647Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M102.095 17C100.632 17 99.494 18.4315 99.6024 20.112L100.415 30.195C100.469 31.1909 101.228 32 102.095 32C102.962 32 103.721 31.1909 103.775 30.195L104.588 20.112C104.696 18.3693 103.558 17 102.095 17Z" fill="#575757"/>
<path d="M102.095 37C100.706 37 99.5952 38.1111 99.5952 39.5C99.5952 40.8889 100.706 42 102.095 42C103.484 42 104.595 40.8889 104.595 39.5C104.595 38.1111 103.484 37 102.095 37Z" fill="#575757"/>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -1,17 +1,17 @@
<svg width="128" height="111" viewBox="0 0 128 111" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="128" height="110" viewBox="0 0 128 110" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M63.6998 109.492C93.3998 109.492 117.6 85.3922 117.6 55.7922C117.5 26.0922 93.3998 1.99219 63.6998 1.99219C33.8998 1.99219 9.7998 26.0922 9.7998 55.6922C9.7998 85.3922 33.8998 109.492 63.6998 109.492Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M63.7001 108.492C93.4001 108.492 117.6 84.3922 117.6 54.7922C117.5 25.0922 93.4001 0.992188 63.7001 0.992188C33.9001 0.992188 9.80005 25.0922 9.80005 54.6922C9.80005 84.3922 33.9001 108.492 63.7001 108.492Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M118.9 36.992C121.164 36.992 123 35.1564 123 32.892C123 30.6276 121.164 28.792 118.9 28.792C116.635 28.792 114.8 30.6276 114.8 32.892C114.8 35.1564 116.635 36.992 118.9 36.992Z" fill="#525252"/> <path d="M118.9 35.992C121.164 35.992 123 34.1564 123 31.892C123 29.6276 121.164 27.792 118.9 27.792C116.636 27.792 114.8 29.6276 114.8 31.892C114.8 34.1564 116.636 35.992 118.9 35.992Z" fill="#575757"/>
<path d="M124.9 20.9921C126.446 20.9921 127.7 19.7385 127.7 18.1921C127.7 16.6457 126.446 15.3921 124.9 15.3921C123.354 15.3921 122.1 16.6457 122.1 18.1921C122.1 19.7385 123.354 20.9921 124.9 20.9921Z" fill="#525252"/> <path d="M124.9 19.9921C126.446 19.9921 127.7 18.7385 127.7 17.1921C127.7 15.6457 126.446 14.3921 124.9 14.3921C123.354 14.3921 122.1 15.6457 122.1 17.1921C122.1 18.7385 123.354 19.9921 124.9 19.9921Z" fill="#575757"/>
<path d="M5.2 91.7921C8.07188 91.7921 10.4 89.464 10.4 86.5921C10.4 83.7202 8.07188 81.3921 5.2 81.3921C2.32812 81.3921 0 83.7202 0 86.5921C0 89.464 2.32812 91.7921 5.2 91.7921Z" fill="#525252"/> <path d="M5.2 90.7921C8.07188 90.7921 10.4 88.464 10.4 85.5921C10.4 82.7202 8.07188 80.3921 5.2 80.3921C2.32812 80.3921 0 82.7202 0 85.5921C0 88.464 2.32812 90.7921 5.2 90.7921Z" fill="#575757"/>
<path d="M101.512 33.162V98.2932C101.512 103.039 97.7479 106.803 93.0022 106.803H34.4168C29.6711 106.803 25.9072 103.039 25.9072 98.2932V13.0335C25.9072 8.28779 29.6711 4.52393 34.4168 4.52393H73.0373C75.1647 4.52393 77.1285 5.34216 78.6013 6.81497L99.2207 27.4344C100.694 29.0709 101.512 31.0346 101.512 33.162Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M101.512 32.162V97.2932C101.512 102.039 97.7477 105.803 93.0019 105.803H34.4166C29.6708 105.803 25.907 102.039 25.907 97.2932V12.0335C25.907 7.28779 29.6708 3.52393 34.4166 3.52393H73.0371C75.1645 3.52393 77.1283 4.34216 78.6011 5.81497L99.2205 26.4344C100.693 28.0709 101.512 30.0346 101.512 32.162Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M75.9185 5.11914V24.4727C75.9185 27.9435 78.8576 30.7201 82.1974 30.7201H100.924" fill="#525252"/> <path d="M75.9182 4.11914V23.4727C75.9182 26.9435 78.8573 29.7201 82.1972 29.7201H100.924" fill="#575757"/>
<path d="M51.1871 89.0665H37.5406C37.0314 89.0665 36.624 88.5253 36.624 87.8758C36.624 87.2263 37.0314 86.6851 37.5406 86.6851H51.1871C51.6963 86.6851 52.1037 87.2263 52.1037 87.8758C52.1037 88.6335 51.6963 89.0665 51.1871 89.0665Z" fill="#525252"/> <path d="M51.1869 88.0665H37.5403C37.0311 88.0665 36.6238 87.5253 36.6238 86.8758C36.6238 86.2263 37.0311 85.6851 37.5403 85.6851H51.1869C51.6961 85.6851 52.1034 86.2263 52.1034 86.8758C52.1034 87.6335 51.6961 88.0665 51.1869 88.0665Z" fill="#575757"/>
<path d="M68.6393 22.3849H37.9495C37.2358 22.3849 36.624 21.8353 36.624 21.1942C36.624 20.553 37.2358 20.0034 37.9495 20.0034H68.6393C69.353 20.0034 69.9648 20.553 69.9648 21.1942C69.9648 21.8353 69.353 22.3849 68.6393 22.3849Z" fill="#525252"/> <path d="M68.6391 21.3849H37.9493C37.2355 21.3849 36.6238 20.8353 36.6238 20.1942C36.6238 19.553 37.2355 19.0034 37.9493 19.0034H68.6391C69.3528 19.0034 69.9646 19.553 69.9646 20.1942C69.9646 20.8353 69.3528 21.3849 68.6391 21.3849Z" fill="#575757"/>
<path d="M49.6968 29.5294H37.9414C37.2321 29.5294 36.624 28.9799 36.624 28.3387C36.624 27.6975 37.2321 27.1479 37.9414 27.1479H49.5955C50.3049 27.1479 50.9129 27.6975 50.9129 28.3387C50.9129 28.9799 50.3049 29.5294 49.6968 29.5294Z" fill="#525252"/> <path d="M49.6966 28.5294H37.9412C37.2318 28.5294 36.6238 27.9799 36.6238 27.3387C36.6238 26.6975 37.2318 26.1479 37.9412 26.1479H49.5953C50.3046 26.1479 50.9127 26.6975 50.9127 27.3387C50.9127 27.9799 50.3046 28.5294 49.6966 28.5294Z" fill="#575757"/>
<path d="M90.4363 71.2056H54.6499H50.7057H38.7769C38.2959 71.2056 37.8149 71.7017 37.8149 72.3963C37.8149 72.9917 38.1997 73.5871 38.7769 73.5871H50.7057H54.6499H90.4363C90.9173 73.5871 91.3983 73.0909 91.3983 72.3963C91.3021 71.7017 90.9173 71.2056 90.4363 71.2056Z" fill="#525252"/> <path d="M90.4358 70.2056H54.6494H50.7053H38.7765C38.2955 70.2056 37.8145 70.7017 37.8145 71.3963C37.8145 71.9917 38.1993 72.5871 38.7765 72.5871H50.7053H54.6494H90.4358C90.9168 72.5871 91.3978 72.0909 91.3978 71.3963C91.3016 70.7017 90.9168 70.2056 90.4358 70.2056Z" fill="#575757"/>
<path d="M90.4346 62.8701H77.1351H72.6056H38.7787C38.2968 62.8701 37.8149 63.3663 37.8149 64.0609C37.8149 64.6562 38.2004 65.2516 38.7787 65.2516H72.6056H77.1351H90.4346C90.9165 65.2516 91.3983 64.7555 91.3983 64.0609C91.302 63.3663 90.9165 62.8701 90.4346 62.8701Z" fill="#525252"/> <path d="M90.4341 61.8701H77.1346H72.6051H38.7782C38.2963 61.8701 37.8145 62.3663 37.8145 63.0609C37.8145 63.6562 38.1999 64.2516 38.7782 64.2516H72.6051H77.1346H90.4341C90.916 64.2516 91.3978 63.7555 91.3978 63.0609C91.3015 62.3663 90.916 61.8701 90.4341 61.8701Z" fill="#575757"/>
<path d="M90.5219 54.5352H87.0657H83.6095H38.775C38.295 54.5352 37.8149 55.0313 37.8149 55.7259C37.8149 56.3213 38.199 56.9166 38.775 56.9166H83.6095H87.4497H90.4259C90.9059 56.9166 91.386 56.4205 91.386 55.7259C91.482 55.1305 91.0019 54.5352 90.5219 54.5352Z" fill="#525252"/> <path d="M90.5214 53.5352H87.0652H83.609H38.7745C38.2945 53.5352 37.8145 54.0313 37.8145 54.7259C37.8145 55.3213 38.1985 55.9166 38.7745 55.9166H83.609H87.4493H90.4254C90.9055 55.9166 91.3855 55.4205 91.3855 54.7259C91.4815 54.1305 91.0015 53.5352 90.5214 53.5352Z" fill="#575757"/>
<path d="M90.4466 46.1997H79.3111H76.5511H38.7667C38.2908 46.1997 37.8149 46.6959 37.8149 47.3905C37.8149 47.9858 38.1956 48.5812 38.7667 48.5812H76.5511H79.3111H90.3514C91.0176 48.5812 91.3983 48.0851 91.3983 47.3905C91.3983 46.7951 91.0176 46.1997 90.4466 46.1997Z" fill="#525252"/> <path d="M90.4461 45.1997H79.3106H76.5506H38.7662C38.2903 45.1997 37.8145 45.6959 37.8145 46.3905C37.8145 46.9858 38.1952 47.5812 38.7662 47.5812H76.5506H79.3106H90.3509C91.0171 47.5812 91.3978 47.0851 91.3978 46.3905C91.3978 45.7951 91.0171 45.1997 90.4461 45.1997Z" fill="#575757"/>
<path d="M91.0478 106.992C102.671 106.992 112.093 97.5698 112.093 85.9468C112.093 74.3237 102.671 64.9014 91.0478 64.9014C79.4248 64.9014 70.0024 74.3237 70.0024 85.9468C70.0024 97.5698 79.4248 106.992 91.0478 106.992Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M91.0475 105.992C102.671 105.992 112.093 96.5698 112.093 84.9468C112.093 73.3237 102.671 63.9014 91.0475 63.9014C79.4244 63.9014 70.0021 73.3237 70.0021 84.9468C70.0021 96.5698 79.4244 105.992 91.0475 105.992Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M92.2581 77.4039C92.3093 77.4659 92.3547 77.533 92.4099 77.5916L97.5979 83.1C98.218 83.7584 98.218 84.9106 97.5979 85.5691C96.9778 86.2275 95.8926 86.2275 95.1949 85.5691L94.5086 84.8639C93.8826 84.2207 92.7919 84.6638 92.7919 85.5613V92.1533C92.7919 93.141 92.0168 93.964 91.0866 93.964C90.1564 93.964 89.3813 93.141 89.3813 92.1533V85.5613C89.3813 84.6638 88.2906 84.2207 87.6646 84.8639L86.9783 85.5691C86.3582 86.2275 85.273 86.2275 84.5753 85.5691C84.1877 85.1576 84.0327 84.746 84.0327 84.3345C84.0327 83.9603 84.1609 83.4499 84.4757 83.1748C84.538 83.1203 84.604 83.0695 84.6608 83.0092L89.8464 77.5034C90.0789 77.2565 90.6215 76.9272 91.0091 76.9272C91.3638 76.9272 91.9781 77.0651 92.2581 77.4039Z" fill="#525252"/> <path d="M92.2577 76.4039C92.309 76.4659 92.3544 76.533 92.4095 76.5916L97.5975 82.1C98.2176 82.7584 98.2176 83.9106 97.5975 84.5691C96.9774 85.2275 95.8922 85.2275 95.1945 84.5691L94.5082 83.8639C93.8823 83.2207 92.7916 83.6638 92.7916 84.5613V91.1533C92.7916 92.141 92.0164 92.964 91.0862 92.964C90.1561 92.964 89.3809 92.141 89.3809 91.1533V84.5613C89.3809 83.6638 88.2902 83.2207 87.6643 83.8639L86.9779 84.5691C86.3578 85.2275 85.2726 85.2275 84.575 84.5691C84.1874 84.1576 84.0323 83.746 84.0323 83.3345C84.0323 82.9603 84.1606 82.4499 84.4753 82.1748C84.5377 82.1203 84.6037 82.0695 84.6605 82.0092L89.846 76.5034C90.0785 76.2565 90.6211 75.9272 91.0087 75.9272C91.3634 75.9272 91.9778 76.0651 92.2577 76.4039Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -1,23 +1,23 @@
<svg width="159" height="121" viewBox="0 0 159 121" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="159" height="121" viewBox="0 0 159 121" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M82.217 119.151C114.778 119.151 141.176 92.7531 141.176 60.0755C141.176 27.3979 114.662 1 82.217 1C49.6557 1 23.2578 27.3979 23.2578 60.0755C23.2578 92.7531 49.6557 119.151 82.217 119.151Z" fill="#F1F3F9" stroke="#D6DCE8" stroke-width="2" stroke-miterlimit="10"/> <path d="M82.2173 119.151C114.779 119.151 141.176 92.7531 141.176 60.0755C141.176 27.3979 114.662 1 82.2173 1C49.656 1 23.2581 27.3979 23.2581 60.0755C23.2581 92.7531 49.656 119.151 82.2173 119.151Z" fill="#F8F8F8" stroke="#EAEAEA" stroke-width="2" stroke-miterlimit="10"/>
<path d="M148.661 41.3581C151.294 41.3581 153.429 39.2234 153.429 36.5902C153.429 33.9569 151.294 31.8223 148.661 31.8223C146.028 31.8223 143.893 33.9569 143.893 36.5902C143.893 39.2234 146.028 41.3581 148.661 41.3581Z" fill="#525252"/> <path d="M148.661 41.3581C151.294 41.3581 153.429 39.2234 153.429 36.5902C153.429 33.9569 151.294 31.8223 148.661 31.8223C146.028 31.8223 143.893 33.9569 143.893 36.5902C143.893 39.2234 146.028 41.3581 148.661 41.3581Z" fill="#575757"/>
<path d="M155.638 22.7515C157.437 22.7515 158.895 21.2937 158.895 19.4954C158.895 17.6971 157.437 16.2393 155.638 16.2393C153.84 16.2393 152.382 17.6971 152.382 19.4954C152.382 21.2937 153.84 22.7515 155.638 22.7515Z" fill="#525252"/> <path d="M155.638 22.7515C157.437 22.7515 158.895 21.2937 158.895 19.4954C158.895 17.6971 157.437 16.2393 155.638 16.2393C153.84 16.2393 152.382 17.6971 152.382 19.4954C152.382 21.2937 153.84 22.7515 155.638 22.7515Z" fill="#575757"/>
<path d="M25.9329 21.3506C27.7312 21.3506 29.189 19.8928 29.189 18.0945C29.189 16.2962 27.7312 14.8384 25.9329 14.8384C24.1346 14.8384 22.6768 16.2962 22.6768 18.0945C22.6768 19.8928 24.1346 21.3506 25.9329 21.3506Z" fill="#525252"/> <path d="M25.9329 21.3506C27.7312 21.3506 29.189 19.8928 29.189 18.0945C29.189 16.2962 27.7312 14.8384 25.9329 14.8384C24.1346 14.8384 22.6768 16.2962 22.6768 18.0945C22.6768 19.8928 24.1346 21.3506 25.9329 21.3506Z" fill="#575757"/>
<path d="M6.0471 84.1479C9.38682 84.1479 12.0942 81.4405 12.0942 78.1008C12.0942 74.7611 9.38682 72.0537 6.0471 72.0537C2.70738 72.0537 0 74.7611 0 78.1008C0 81.4405 2.70738 84.1479 6.0471 84.1479Z" fill="#525252"/> <path d="M6.0471 84.1479C9.38682 84.1479 12.0942 81.4405 12.0942 78.1008C12.0942 74.7611 9.38682 72.0537 6.0471 72.0537C2.70738 72.0537 0 74.7611 0 78.1008C0 81.4405 2.70738 84.1479 6.0471 84.1479Z" fill="#575757"/>
<path d="M82.217 119.151C114.778 119.151 141.176 92.7531 141.176 60.0755C141.176 27.3979 114.662 1 82.217 1C49.6557 1 23.2578 27.3979 23.2578 60.0755C23.2578 92.7531 49.6557 119.151 82.217 119.151Z" fill="#181818" stroke="#3c3c3c" stroke-width="2" stroke-miterlimit="10"/> <path d="M82.2173 119.151C114.779 119.151 141.176 92.7531 141.176 60.0755C141.176 27.3979 114.662 1 82.2173 1C49.656 1 23.2581 27.3979 23.2581 60.0755C23.2581 92.7531 49.656 119.151 82.2173 119.151Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M130.215 115.858H31.5791C29.6577 115.858 28.0811 114.282 28.0811 112.36V88.2676C28.0811 86.3461 29.6577 84.7695 31.5791 84.7695H130.215C132.137 84.7695 133.713 86.3461 133.713 88.2676V112.36C133.713 114.282 132.137 115.858 130.215 115.858Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M130.216 115.858H31.5794C29.6579 115.858 28.0813 114.282 28.0813 112.36V88.2676C28.0813 86.3461 29.6579 84.7695 31.5794 84.7695H130.216C132.137 84.7695 133.714 86.3461 133.714 88.2676V112.36C133.714 114.282 132.137 115.858 130.216 115.858Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M113.858 96.4461H58.9232C57.7408 96.4461 56.8047 95.7277 56.8047 94.8202C56.8047 93.9127 57.7408 93.1943 58.9232 93.1943H113.858C115.04 93.1943 115.977 93.9127 115.977 94.8202C115.977 95.7277 115.04 96.4461 113.858 96.4461Z" fill="#3c3c3c"/> <path d="M113.859 96.4461H58.9237C57.7413 96.4461 56.8052 95.7277 56.8052 94.8202C56.8052 93.9127 57.7413 93.1943 58.9237 93.1943H113.859C115.041 93.1943 115.977 93.9127 115.977 94.8202C115.977 95.7277 115.041 96.4461 113.859 96.4461Z" fill="#575757"/>
<path d="M94.1505 106.349H58.9232C57.7408 106.349 56.8047 105.631 56.8047 104.724C56.8047 103.816 57.7408 103.098 58.9232 103.098H94.1505C95.3329 103.098 96.269 103.816 96.269 104.724C96.269 105.631 95.3329 106.349 94.1505 106.349Z" fill="#3c3c3c"/> <path d="M94.151 106.349H58.9237C57.7413 106.349 56.8052 105.631 56.8052 104.724C56.8052 103.816 57.7413 103.098 58.9237 103.098H94.151C95.3334 103.098 96.2695 103.816 96.2695 104.724C96.2695 105.631 95.3334 106.349 94.151 106.349Z" fill="#575757"/>
<path d="M42.6649 109.059C47.5083 109.059 51.4347 105.133 51.4347 100.289C51.4347 95.4459 47.5083 91.5195 42.6649 91.5195C37.8214 91.5195 33.895 95.4459 33.895 100.289C33.895 105.133 37.8214 109.059 42.6649 109.059Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M42.6649 109.059C47.5083 109.059 51.4347 105.133 51.4347 100.289C51.4347 95.4459 47.5083 91.5195 42.6649 91.5195C37.8214 91.5195 33.895 95.4459 33.895 100.289C33.895 105.133 37.8214 109.059 42.6649 109.059Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M44.8024 101.024C44.4076 100.632 44.4076 99.9943 44.8024 99.6031L45.4178 98.9931C45.8101 98.6044 45.813 97.9712 45.4242 97.5789V97.5789C45.0354 97.1867 44.4023 97.1838 44.01 97.5726L43.3691 98.2078C42.9793 98.5941 42.351 98.5941 41.9612 98.2078L41.3203 97.5726C40.928 97.1838 40.2948 97.1867 39.9061 97.5789V97.5789C39.5173 97.9712 39.5201 98.6044 39.9124 98.9931L40.5279 99.6031C40.9226 99.9943 40.9226 100.632 40.5279 101.024L39.9124 101.634C39.5201 102.022 39.5173 102.655 39.9061 103.048V103.048C40.2948 103.44 40.928 103.443 41.3203 103.054L41.9612 102.419C42.351 102.033 42.9793 102.033 43.3691 102.419L44.01 103.054C44.4023 103.443 45.0354 103.44 45.4242 103.048V103.048C45.813 102.655 45.8101 102.022 45.4178 101.634L44.8024 101.024Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M44.8024 101.024C44.4076 100.632 44.4076 99.9943 44.8024 99.6031L45.4178 98.9931C45.8101 98.6044 45.813 97.9712 45.4242 97.5789C45.0354 97.1867 44.4023 97.1838 44.01 97.5726L43.3691 98.2078C42.9793 98.5941 42.351 98.5941 41.9612 98.2078L41.3203 97.5726C40.928 97.1838 40.2948 97.1867 39.9061 97.5789C39.5173 97.9712 39.5201 98.6044 39.9124 98.9931L40.5279 99.6031C40.9226 99.9943 40.9226 100.632 40.5279 101.024L39.9124 101.634C39.5201 102.022 39.5173 102.655 39.9061 103.048C40.2948 103.44 40.928 103.443 41.3203 103.054L41.9612 102.419C42.351 102.033 42.9793 102.033 43.3691 102.419L44.01 103.054C44.4023 103.443 45.0354 103.44 45.4242 103.048C45.813 102.655 45.8101 102.022 45.4178 101.634L44.8024 101.024Z" fill="#575757"/>
<path d="M133.53 36.5091H34.8936C32.9721 36.5091 31.3955 34.9325 31.3955 33.011V8.9185C31.3955 6.99701 32.9721 5.42041 34.8936 5.42041H133.53C135.451 5.42041 137.028 6.99701 137.028 8.9185V33.011C137.028 34.9325 135.451 36.5091 133.53 36.5091Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M133.53 36.5091H34.8936C32.9721 36.5091 31.3955 34.9325 31.3955 33.011V8.9185C31.3955 6.99701 32.9721 5.42041 34.8936 5.42041H133.53C135.451 5.42041 137.028 6.99701 137.028 8.9185V33.011C137.028 34.9325 135.451 36.5091 133.53 36.5091Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M117.172 17.097H62.2377C61.0552 17.097 60.1191 16.3785 60.1191 15.4711C60.1191 14.5636 61.0552 13.8452 62.2377 13.8452H117.172C118.355 13.8452 119.291 14.5636 119.291 15.4711C119.291 16.3785 118.355 17.097 117.172 17.097Z" fill="#3c3c3c"/> <path d="M117.173 17.097H62.2379C61.0555 17.097 60.1194 16.3785 60.1194 15.4711C60.1194 14.5636 61.0555 13.8452 62.2379 13.8452H117.173C118.355 13.8452 119.291 14.5636 119.291 15.4711C119.291 16.3785 118.355 17.097 117.173 17.097Z" fill="#575757"/>
<path d="M97.4649 27.0003H62.2377C61.0552 27.0003 60.1191 26.2819 60.1191 25.3744C60.1191 24.4669 61.0552 23.7485 62.2377 23.7485H97.4649C98.6474 23.7485 99.5835 24.4669 99.5835 25.3744C99.5835 26.2819 98.6474 27.0003 97.4649 27.0003Z" fill="#3c3c3c"/> <path d="M97.4652 27.0003H62.2379C61.0555 27.0003 60.1194 26.2819 60.1194 25.3744C60.1194 24.4669 61.0555 23.7485 62.2379 23.7485H97.4652C98.6476 23.7485 99.5837 24.4669 99.5837 25.3744C99.5837 26.2819 98.6476 27.0003 97.4652 27.0003Z" fill="#575757"/>
<path d="M45.9788 29.7096C50.8223 29.7096 54.7487 25.7832 54.7487 20.9398C54.7487 16.0963 50.8223 12.1699 45.9788 12.1699C41.1354 12.1699 37.209 16.0963 37.209 20.9398C37.209 25.7832 41.1354 29.7096 45.9788 29.7096Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M45.9788 29.7096C50.8223 29.7096 54.7487 25.7832 54.7487 20.9398C54.7487 16.0963 50.8223 12.1699 45.9788 12.1699C41.1354 12.1699 37.209 16.0963 37.209 20.9398C37.209 25.7832 41.1354 29.7096 45.9788 29.7096Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M48.1164 21.6745C47.7216 21.2833 47.7216 20.6452 48.1164 20.254L48.7318 19.644C49.1241 19.2552 49.1269 18.6221 48.7382 18.2298V18.2298C48.3494 17.8375 47.7162 17.8347 47.324 18.2235L46.683 18.8587C46.2932 19.245 45.665 19.245 45.2752 18.8587L44.6342 18.2235C44.242 17.8347 43.6088 17.8375 43.22 18.2298V18.2298C42.8313 18.6221 42.8341 19.2552 43.2264 19.644L43.8418 20.254C44.2366 20.6452 44.2366 21.2833 43.8418 21.6745L43.2264 22.2844C42.8341 22.6732 42.8313 23.3064 43.22 23.6986V23.6986C43.6088 24.0909 44.242 24.0938 44.6342 23.705L45.2752 23.0698C45.665 22.6835 46.2932 22.6835 46.683 23.0698L47.324 23.705C47.7162 24.0938 48.3494 24.0909 48.7382 23.6986V23.6986C49.1269 23.3064 49.1241 22.6732 48.7318 22.2844L48.1164 21.6745Z" fill="#525252"/> <path d="M48.1164 21.6745C47.7216 21.2833 47.7216 20.6452 48.1164 20.254L48.7318 19.644C49.1241 19.2552 49.1269 18.6221 48.7382 18.2298C48.3494 17.8375 47.7162 17.8347 47.324 18.2235L46.683 18.8587C46.2932 19.245 45.665 19.245 45.2752 18.8587L44.6342 18.2235C44.242 17.8347 43.6088 17.8375 43.22 18.2298C42.8313 18.6221 42.8341 19.2552 43.2264 19.644L43.8418 20.254C44.2366 20.6452 44.2366 21.2833 43.8418 21.6745L43.2264 22.2844C42.8341 22.6732 42.8313 23.3064 43.22 23.6986C43.6088 24.0909 44.242 24.0938 44.6342 23.705L45.2752 23.0698C45.665 22.6835 46.2932 22.6835 46.683 23.0698L47.324 23.705C47.7162 24.0938 48.3494 24.0909 48.7382 23.6986C49.1269 23.3064 49.1241 22.6732 48.7318 22.2844L48.1164 21.6745Z" fill="#575757"/>
<path d="M118.391 75.9969H19.7056C17.7841 75.9969 16.2075 74.4202 16.2075 72.4988V48.4063C16.2075 46.4848 17.7841 44.9082 19.7056 44.9082H118.342C120.263 44.9082 121.84 46.4848 121.84 48.4063V72.4988C121.889 74.4695 120.313 75.9969 118.391 75.9969Z" fill="#181818" stroke="#525252" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M118.391 75.9969H19.7056C17.7841 75.9969 16.2075 74.4202 16.2075 72.4988V48.4063C16.2075 46.4848 17.7841 44.9082 19.7056 44.9082H118.342C120.263 44.9082 121.84 46.4848 121.84 48.4063V72.4988C121.889 74.4695 120.313 75.9969 118.391 75.9969Z" fill="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
<path d="M30.8407 69.2472C35.6841 69.2472 39.6105 65.3208 39.6105 60.4774C39.6105 55.6339 35.6841 51.7075 30.8407 51.7075C25.9972 51.7075 22.0708 55.6339 22.0708 60.4774C22.0708 65.3208 25.9972 69.2472 30.8407 69.2472Z" fill="#181818" stroke="#737373" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/> <path d="M30.8404 69.2472C35.6839 69.2472 39.6103 65.3208 39.6103 60.4774C39.6103 55.6339 35.6839 51.7075 30.8404 51.7075C25.997 51.7075 22.0706 55.6339 22.0706 60.4774C22.0706 65.3208 25.997 69.2472 30.8404 69.2472Z" fill="#1E1E1E" stroke="#545454" stroke-width="2" stroke-miterlimit="10" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.9789 57.7183C35.3694 58.1089 35.3694 58.742 34.9789 59.1325L30.1679 63.9436C29.7774 64.3341 29.1442 64.3341 28.7537 63.9436L26.7017 61.8916C26.3112 61.5011 26.3112 60.8679 26.7017 60.4774V60.4774C27.0923 60.0869 27.7254 60.0869 28.1159 60.4774L28.7537 61.1151C29.1442 61.5057 29.7774 61.5057 30.1679 61.1151L33.5647 57.7183C33.9552 57.3278 34.5884 57.3278 34.9789 57.7183V57.7183Z" fill="#525252"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M34.9789 57.7183C35.3694 58.1089 35.3694 58.742 34.9789 59.1326L30.1679 63.9436C29.7774 64.3341 29.1442 64.3341 28.7537 63.9436L26.7017 61.8916C26.3112 61.5011 26.3112 60.8679 26.7017 60.4774C27.0923 60.0869 27.7254 60.0869 28.1159 60.4774L28.7537 61.1151C29.1442 61.5057 29.7774 61.5057 30.1679 61.1151L33.5647 57.7183C33.9552 57.3278 34.5884 57.3278 34.9789 57.7183Z" fill="#575757"/>
<path d="M102.034 56.5852H47.099C45.9166 56.5852 44.9805 55.8668 44.9805 54.9594C44.9805 54.0519 45.9166 53.3335 47.099 53.3335H102.034C103.216 53.3335 104.152 54.0519 104.152 54.9594C104.152 55.8668 103.167 56.5852 102.034 56.5852Z" fill="#3c3c3c"/> <path d="M102.034 56.5852H47.0993C45.9168 56.5852 44.9807 55.8668 44.9807 54.9594C44.9807 54.0519 45.9168 53.3335 47.0993 53.3335H102.034C103.216 53.3335 104.153 54.0519 104.153 54.9594C104.153 55.8668 103.167 56.5852 102.034 56.5852Z" fill="#575757"/>
<path d="M82.3263 66.5374H47.099C45.9166 66.5374 44.9805 65.819 44.9805 64.9115C44.9805 64.0041 45.9166 63.2856 47.099 63.2856H82.3263C83.5087 63.2856 84.4448 64.0041 84.4448 64.9115C84.3956 65.819 83.4594 66.5374 82.3263 66.5374Z" fill="#3c3c3c"/> <path d="M82.3265 66.5374H47.0993C45.9168 66.5374 44.9807 65.819 44.9807 64.9115C44.9807 64.0041 45.9168 63.2856 47.0993 63.2856H82.3265C83.509 63.2856 84.4451 64.0041 84.4451 64.9115C84.3958 65.819 83.4597 66.5374 82.3265 66.5374Z" fill="#575757"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -1,76 +1,76 @@
<svg width="148" height="163" viewBox="0 0 148 163" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="148" height="163" viewBox="0 0 148 163" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M56.1607 15.3833C25.4726 23.8793 7.48238 55.6443 15.9784 86.3324C24.4744 117.02 56.2394 135.011 86.9275 126.515C117.616 118.019 135.606 86.2537 127.11 55.5656C118.614 24.8776 86.8488 6.88731 56.1607 15.3833Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M56.161 15.3833C25.4729 23.8793 7.48263 55.6443 15.9786 86.3324C24.4746 117.02 56.2396 135.011 86.9277 126.515C117.616 118.019 135.606 86.2537 127.11 55.5656C118.614 24.8776 86.8491 6.88731 56.161 15.3833Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M91.7717 32.1479L92.8659 31.5956C92.9238 31.5796 93.0236 31.4896 92.9916 31.3737L92.9026 30.1513C92.9124 29.9615 92.9961 29.8136 93.1699 29.7655C93.3436 29.7174 93.5495 29.7851 93.5976 29.9588L94.1499 31.053C94.166 31.111 94.2559 31.2108 94.3718 31.1787L95.5943 31.0897C95.784 31.0995 95.9319 31.1833 95.98 31.357C96.0282 31.5308 95.9604 31.7366 95.7867 31.7847L94.6925 32.337C94.6345 32.3531 94.5347 32.4431 94.5668 32.5589L94.6558 33.7814C94.646 33.9712 94.5623 34.1191 94.3885 34.1672C94.2147 34.2153 94.0089 34.1475 93.9608 33.9738L93.4085 32.8796C93.3924 32.8217 93.3024 32.7219 93.1866 32.7539L91.9641 32.843C91.7743 32.8331 91.6264 32.7494 91.5783 32.5756C91.5463 32.4598 91.614 32.254 91.7717 32.1479Z" fill="#181818"/> <path d="M91.7718 32.1479L92.866 31.5956C92.924 31.5796 93.0238 31.4896 92.9917 31.3737L92.9027 30.1513C92.9125 29.9615 92.9963 29.8136 93.17 29.7655C93.3438 29.7174 93.5496 29.7851 93.5977 29.9588L94.15 31.053C94.1661 31.111 94.2561 31.2108 94.3719 31.1787L95.5944 31.0897C95.7842 31.0995 95.9321 31.1833 95.9802 31.357C96.0283 31.5308 95.9605 31.7366 95.7868 31.7847L94.6926 32.337C94.6347 32.3531 94.5349 32.4431 94.5669 32.5589L94.656 33.7814C94.6461 33.9712 94.5624 34.1191 94.3886 34.1672C94.2149 34.2153 94.009 34.1475 93.9609 33.9738L93.4086 32.8796C93.3926 32.8217 93.3026 32.7219 93.1867 32.7539L91.9642 32.843C91.7745 32.8331 91.6266 32.7494 91.5785 32.5756C91.5464 32.4598 91.6141 32.254 91.7718 32.1479Z" fill="#1E1E1E"/>
<path d="M39.2363 38.4401L40.2794 37.9135C40.3347 37.8982 40.4298 37.8124 40.3992 37.702L40.3144 36.5366C40.3237 36.3556 40.4036 36.2146 40.5692 36.1688C40.7349 36.1229 40.9311 36.1875 40.9769 36.3531L41.5035 37.3963C41.5188 37.4515 41.6046 37.5466 41.715 37.516L42.8804 37.4312C43.0614 37.4405 43.2024 37.5204 43.2482 37.686C43.2941 37.8517 43.2295 38.0479 43.0639 38.0938L42.0207 38.6203C41.9655 38.6356 41.8704 38.7214 41.9009 38.8318L41.9858 39.9973C41.9765 40.1782 41.8966 40.3192 41.731 40.3651C41.5653 40.4109 41.3691 40.3463 41.3232 40.1807L40.7967 39.1376C40.7814 39.0823 40.6956 38.9872 40.5852 39.0178L39.4197 39.1026C39.2388 39.0933 39.0978 39.0134 39.0519 38.8478C39.0061 38.6821 39.0707 38.4859 39.2363 38.4401Z" fill="#181818"/> <path d="M39.2365 38.4401L40.2797 37.9135C40.3349 37.8982 40.43 37.8124 40.3995 37.702L40.3146 36.5366C40.324 36.3556 40.4038 36.2146 40.5695 36.1688C40.7351 36.1229 40.9313 36.1875 40.9772 36.3531L41.5038 37.3963C41.519 37.4515 41.6048 37.5466 41.7153 37.516L42.8807 37.4312C43.0616 37.4405 43.2026 37.5204 43.2485 37.686C43.2943 37.8517 43.2298 38.0479 43.0641 38.0938L42.021 38.6203C41.9658 38.6356 41.8706 38.7214 41.9012 38.8318L41.9861 39.9973C41.9767 40.1782 41.8968 40.3192 41.7312 40.3651C41.5656 40.4109 41.3693 40.3463 41.3235 40.1807L40.7969 39.1376C40.7816 39.0823 40.6958 38.9872 40.5854 39.0178L39.42 39.1026C39.2391 39.0933 39.098 39.0134 39.0522 38.8478C39.0063 38.6821 39.0709 38.4859 39.2365 38.4401Z" fill="#1E1E1E"/>
<path d="M76.6054 49.8626L77.448 49.4373C77.4926 49.425 77.5694 49.3557 77.5447 49.2665L77.4762 48.3252C77.4837 48.1791 77.5482 48.0652 77.682 48.0282C77.8158 47.9911 77.9743 48.0433 78.0113 48.1771L78.4366 49.0196C78.449 49.0642 78.5183 49.141 78.6074 49.1163L79.5487 49.0478C79.6949 49.0553 79.8087 49.1198 79.8458 49.2536C79.8828 49.3874 79.8307 49.5459 79.6969 49.5829L78.8544 50.0082C78.8098 50.0206 78.7329 50.0899 78.7576 50.1791L78.8262 51.1203C78.8186 51.2665 78.7541 51.3804 78.6203 51.4174C78.4865 51.4544 78.3281 51.4023 78.291 51.2685L77.8657 50.426C77.8534 50.3814 77.7841 50.3045 77.6949 50.3292L76.7536 50.3978C76.6075 50.3902 76.4936 50.3257 76.4565 50.1919C76.3873 50.1151 76.484 49.9443 76.6054 49.8626Z" fill="#181818"/> <path d="M76.6054 49.8626L77.448 49.4373C77.4926 49.425 77.5694 49.3557 77.5447 49.2665L77.4762 48.3252C77.4837 48.1791 77.5482 48.0652 77.682 48.0282C77.8158 47.9911 77.9743 48.0433 78.0113 48.1771L78.4366 49.0196C78.449 49.0642 78.5183 49.141 78.6074 49.1163L79.5487 49.0478C79.6949 49.0553 79.8087 49.1198 79.8458 49.2536C79.8828 49.3874 79.8307 49.5459 79.6969 49.5829L78.8544 50.0082C78.8098 50.0206 78.7329 50.0899 78.7576 50.1791L78.8262 51.1203C78.8186 51.2665 78.7541 51.3804 78.6203 51.4174C78.4865 51.4544 78.3281 51.4023 78.291 51.2685L77.8657 50.426C77.8534 50.3814 77.7841 50.3045 77.6949 50.3292L76.7536 50.3978C76.6075 50.3902 76.4936 50.3257 76.4565 50.1919C76.3873 50.1151 76.484 49.9443 76.6054 49.8626Z" fill="#1E1E1E"/>
<path d="M43.4125 109.199L44.9251 108.436C45.0051 108.414 45.1431 108.289 45.0988 108.129L44.9757 106.439C44.9893 106.177 45.1051 105.972 45.3453 105.906C45.5855 105.839 45.87 105.933 45.9365 106.173L46.7 107.686C46.7222 107.766 46.8466 107.904 47.0067 107.86L48.6967 107.736C48.959 107.75 49.1635 107.866 49.23 108.106C49.2965 108.346 49.2028 108.631 48.9627 108.697L47.45 109.461C47.37 109.483 47.232 109.607 47.2763 109.767L47.3994 111.457C47.3858 111.72 47.27 111.924 47.0299 111.991C46.7897 112.057 46.5051 111.964 46.4387 111.723L45.6751 110.211C45.6529 110.131 45.5285 109.993 45.3684 110.037L43.6785 110.16C43.4161 110.147 43.2116 110.031 43.1451 109.791C43.0787 109.55 43.1944 109.346 43.4125 109.199Z" fill="#181818"/> <path d="M43.4127 109.199L44.9253 108.436C45.0054 108.414 45.1433 108.289 45.099 108.129L44.976 106.439C44.9895 106.177 45.1053 105.972 45.3455 105.906C45.5857 105.839 45.8702 105.933 45.9367 106.173L46.7003 107.686C46.7225 107.766 46.8469 107.904 47.007 107.86L48.6969 107.736C48.9593 107.75 49.1637 107.866 49.2302 108.106C49.2967 108.346 49.2031 108.631 48.9629 108.697L47.4503 109.461C47.3702 109.483 47.2323 109.607 47.2766 109.767L47.3997 111.457C47.3861 111.72 47.2703 111.924 47.0301 111.991C46.7899 112.057 46.5054 111.964 46.4389 111.723L45.6753 110.211C45.6532 110.131 45.5288 109.993 45.3686 110.037L43.6787 110.16C43.4163 110.147 43.2119 110.031 43.1454 109.791C43.0789 109.55 43.1947 109.346 43.4127 109.199Z" fill="#1E1E1E"/>
<path d="M120.156 69.1737L120.948 67.985C120.998 67.9355 121.047 67.7869 120.948 67.6878L120.156 66.4991C120.057 66.301 120.057 66.1029 120.205 65.9543C120.354 65.8057 120.601 65.7562 120.75 65.9048L121.939 66.6973C121.988 66.7468 122.137 66.7963 122.236 66.6973L123.425 65.9048C123.623 65.8057 123.821 65.8057 123.969 65.9543C124.118 66.1029 124.168 66.3506 124.019 66.4991L123.227 67.6878C123.177 67.7374 123.127 67.886 123.227 67.985L124.019 69.1737C124.118 69.3718 124.118 69.5699 123.969 69.7185C123.821 69.8671 123.573 69.9167 123.425 69.7681L122.236 68.9756C122.186 68.9261 122.038 68.8765 121.939 68.9756L120.75 69.7681C120.552 69.8671 120.354 69.8671 120.205 69.7185C120.057 69.57 120.007 69.3223 120.156 69.1737Z" fill="#181818"/> <path d="M120.156 69.1737L120.948 67.985C120.998 67.9355 121.047 67.7869 120.948 67.6878L120.156 66.4991C120.057 66.301 120.057 66.1029 120.205 65.9543C120.354 65.8057 120.601 65.7562 120.75 65.9048L121.939 66.6973C121.988 66.7468 122.137 66.7963 122.236 66.6973L123.425 65.9048C123.623 65.8057 123.821 65.8057 123.969 65.9543C124.118 66.1029 124.168 66.3506 124.019 66.4991L123.227 67.6878C123.177 67.7374 123.127 67.886 123.227 67.985L124.019 69.1737C124.118 69.3718 124.118 69.5699 123.969 69.7185C123.821 69.8671 123.573 69.9167 123.425 69.7681L122.236 68.9756C122.186 68.9261 122.038 68.8765 121.939 68.9756L120.75 69.7681C120.552 69.8671 120.354 69.8671 120.205 69.7185C120.057 69.57 120.007 69.3223 120.156 69.1737Z" fill="#1E1E1E"/>
<path d="M43.5537 60.6946L44.5678 58.6607C44.6361 58.5727 44.6847 58.3283 44.5086 58.1917L43.0212 56.4733C42.8254 56.1803 42.786 55.8676 42.9909 55.6035C43.1958 55.3394 43.5768 55.2119 43.8409 55.4169L45.8749 56.431C45.9629 56.4993 46.2073 56.5479 46.3439 56.3718L48.0623 54.8844C48.3553 54.6886 48.6679 54.6492 48.932 54.8541C49.1961 55.059 49.3236 55.44 49.1187 55.7041L48.1046 57.7381C48.0363 57.8261 47.9877 58.0705 48.1638 58.2071L49.6512 59.9255C49.847 60.2184 49.8864 60.5311 49.6815 60.7952C49.4765 61.0594 49.0956 61.1868 48.8314 60.9819L46.7975 59.9678C46.7095 59.8995 46.4651 59.8509 46.3285 60.0269L44.6101 61.5144C44.3171 61.7102 44.0044 61.7496 43.7403 61.5447C43.4762 61.3397 43.3488 60.9587 43.5537 60.6946Z" fill="#181818"/> <path d="M43.5537 60.6946L44.5678 58.6607C44.6361 58.5727 44.6847 58.3283 44.5086 58.1917L43.0212 56.4733C42.8254 56.1803 42.786 55.8676 42.9909 55.6035C43.1958 55.3394 43.5768 55.2119 43.8409 55.4169L45.8749 56.431C45.9629 56.4993 46.2073 56.5479 46.3439 56.3718L48.0623 54.8844C48.3553 54.6886 48.6679 54.6492 48.932 54.8541C49.1961 55.059 49.3236 55.44 49.1187 55.7041L48.1046 57.7381C48.0363 57.8261 47.9877 58.0705 48.1638 58.2071L49.6512 59.9255C49.847 60.2184 49.8864 60.5311 49.6815 60.7952C49.4765 61.0594 49.0956 61.1868 48.8314 60.9819L46.7975 59.9678C46.7095 59.8995 46.4651 59.8509 46.3285 60.0269L44.6101 61.5144C44.3171 61.7102 44.0044 61.7496 43.7403 61.5447C43.4762 61.3397 43.3488 60.9587 43.5537 60.6946Z" fill="#1E1E1E"/>
<path d="M63.753 120.774L65.1738 120.056C65.249 120.036 65.3785 119.919 65.3369 119.768L65.2213 118.181C65.2341 117.935 65.3428 117.743 65.5684 117.68C65.794 117.618 66.0613 117.706 66.1237 117.931L66.8409 119.352C66.8617 119.427 66.9786 119.557 67.129 119.515L68.7163 119.4C68.9627 119.412 69.1547 119.521 69.2172 119.747C69.2796 119.972 69.1917 120.239 68.9661 120.302L67.5454 121.019C67.4702 121.04 67.3406 121.157 67.3822 121.307L67.4978 122.894C67.4851 123.141 67.3763 123.333 67.1507 123.395C66.9251 123.458 66.6579 123.37 66.5954 123.144L65.8782 121.724C65.8574 121.648 65.7406 121.519 65.5902 121.56L64.0029 121.676C63.7564 121.663 63.5644 121.554 63.5019 121.329C63.4395 121.103 63.5274 120.836 63.753 120.774Z" fill="#181818"/> <path d="M63.753 120.774L65.1738 120.056C65.249 120.036 65.3785 119.919 65.3369 119.768L65.2213 118.181C65.2341 117.935 65.3428 117.743 65.5684 117.68C65.794 117.618 66.0613 117.706 66.1237 117.931L66.8409 119.352C66.8617 119.427 66.9786 119.557 67.129 119.515L68.7163 119.4C68.9627 119.412 69.1547 119.521 69.2172 119.747C69.2796 119.972 69.1917 120.239 68.9661 120.302L67.5454 121.019C67.4702 121.04 67.3406 121.157 67.3822 121.307L67.4978 122.894C67.4851 123.141 67.3763 123.333 67.1507 123.395C66.9251 123.458 66.6579 123.37 66.5954 123.144L65.8782 121.724C65.8574 121.648 65.7406 121.519 65.5902 121.56L64.0029 121.676C63.7564 121.663 63.5644 121.554 63.5019 121.329C63.4395 121.103 63.5274 120.836 63.753 120.774Z" fill="#1E1E1E"/>
<path d="M36.4421 84.4297L37.4304 82.6771C37.4948 82.6024 37.5488 82.3885 37.3992 82.2598L36.1616 80.6734C36.0017 80.4056 35.9809 80.1274 36.174 79.9031C36.3671 79.6788 36.7097 79.5833 36.934 79.7764L38.6865 80.7647C38.7613 80.829 38.9752 80.883 39.1039 80.7335L40.6902 79.4958C40.9581 79.3359 41.2363 79.3152 41.4606 79.5082C41.6848 79.7013 41.7804 80.0439 41.5873 80.2682L40.599 82.0208C40.5346 82.0955 40.4807 82.3094 40.6302 82.4381L41.8678 84.0244C42.0277 84.2923 42.0485 84.5705 41.8554 84.7948C41.6623 85.0191 41.3197 85.1146 41.0955 84.9215L39.3429 83.9332C39.2681 83.8689 39.0543 83.8149 38.9255 83.9644L37.3392 85.2021C37.0714 85.362 36.7931 85.3828 36.5689 85.1897C36.3446 84.9966 36.2491 84.654 36.4421 84.4297Z" fill="#181818"/> <path d="M36.4424 84.4297L37.4307 82.6771C37.495 82.6024 37.549 82.3885 37.3995 82.2598L36.1618 80.6734C36.0019 80.4056 35.9812 80.1274 36.1742 79.9031C36.3673 79.6788 36.71 79.5833 36.9342 79.7764L38.6868 80.7647C38.7615 80.829 38.9754 80.883 39.1041 80.7335L40.6905 79.4958C40.9583 79.3359 41.2365 79.3152 41.4608 79.5082C41.6851 79.7013 41.7806 80.0439 41.5875 80.2682L40.5993 82.0208C40.5349 82.0955 40.4809 82.3094 40.6304 82.4381L41.8681 84.0244C42.028 84.2923 42.0487 84.5705 41.8557 84.7948C41.6626 85.0191 41.32 85.1146 41.0957 84.9215L39.3431 83.9332C39.2684 83.8689 39.0545 83.8149 38.9258 83.9644L37.3395 85.2021C37.0716 85.362 36.7934 85.3828 36.5691 85.1897C36.3448 84.9966 36.2493 84.654 36.4424 84.4297Z" fill="#1E1E1E"/>
<path d="M61.2319 29.7607L63.0868 28.8648C63.2644 28.8157 63.304 28.6135 63.2794 28.5247L63.0938 26.4732C63.1088 26.1823 63.3014 25.8422 63.5678 25.7684C63.8341 25.6947 64.1496 25.7985 64.3367 26.1291L65.2326 27.984C65.2818 28.1615 65.4839 28.2012 65.5727 28.1766L67.6242 27.991C67.9152 28.006 68.2552 28.1986 68.329 28.465C68.4027 28.7313 68.2989 29.0468 67.9684 29.2339L66.1135 30.1298C65.9359 30.179 65.8963 30.3811 65.9208 30.4699L66.1065 32.5214C66.0914 32.8124 65.8988 33.1524 65.6325 33.2262C65.3661 33.2999 65.0506 33.1961 64.8635 32.8655L63.9676 31.0106C63.9185 30.8331 63.7163 30.7935 63.6275 30.818L61.576 31.0037C61.2851 30.9886 60.945 30.796 60.8713 30.5296C60.7729 30.1745 60.8768 29.859 61.2319 29.7607Z" fill="#181818"/> <path d="M61.2319 29.7607L63.0868 28.8648C63.2644 28.8157 63.304 28.6135 63.2794 28.5247L63.0938 26.4732C63.1088 26.1823 63.3014 25.8422 63.5678 25.7684C63.8341 25.6947 64.1496 25.7985 64.3367 26.1291L65.2326 27.984C65.2818 28.1615 65.4839 28.2012 65.5727 28.1766L67.6242 27.991C67.9152 28.006 68.2552 28.1986 68.329 28.465C68.4027 28.7313 68.2989 29.0468 67.9684 29.2339L66.1135 30.1298C65.9359 30.179 65.8963 30.3811 65.9208 30.4699L66.1065 32.5214C66.0914 32.8124 65.8988 33.1524 65.6325 33.2262C65.3661 33.2999 65.0506 33.1961 64.8635 32.8655L63.9676 31.0106C63.9185 30.8331 63.7163 30.7935 63.6275 30.818L61.576 31.0037C61.2851 30.9886 60.945 30.796 60.8713 30.5296C60.7729 30.1745 60.8768 29.859 61.2319 29.7607Z" fill="#1E1E1E"/>
<path d="M104.567 49.7554L105.499 49.3054C105.588 49.2807 105.608 49.1792 105.596 49.1346L105.503 48.1041C105.51 47.9579 105.607 47.7871 105.741 47.7501C105.875 47.713 106.033 47.7652 106.127 47.9312L106.577 48.8629C106.602 48.9521 106.703 48.972 106.748 48.9597L107.778 48.8665C107.924 48.874 108.095 48.9708 108.132 49.1045C108.169 49.2383 108.117 49.3968 107.951 49.4908L107.019 49.9408C106.93 49.9655 106.91 50.067 106.923 50.1116L107.016 51.1421C107.008 51.2882 106.912 51.459 106.778 51.4961C106.644 51.5331 106.486 51.481 106.392 51.3149L105.942 50.3832C105.917 50.294 105.815 50.2741 105.771 50.2865L104.74 50.3797C104.594 50.3722 104.423 50.2754 104.386 50.1416C104.349 50.0078 104.446 49.837 104.567 49.7554Z" fill="#181818"/> <path d="M104.568 49.7554L105.499 49.3054C105.589 49.2807 105.608 49.1792 105.596 49.1346L105.503 48.1041C105.51 47.9579 105.607 47.7871 105.741 47.7501C105.875 47.713 106.033 47.7652 106.127 47.9312L106.577 48.8629C106.602 48.9521 106.703 48.972 106.748 48.9597L107.778 48.8665C107.925 48.874 108.095 48.9708 108.132 49.1045C108.169 49.2383 108.117 49.3968 107.951 49.4908L107.02 49.9408C106.93 49.9655 106.911 50.067 106.923 50.1116L107.016 51.1421C107.009 51.2882 106.912 51.459 106.778 51.4961C106.644 51.5331 106.486 51.481 106.392 51.3149L105.942 50.3832C105.917 50.294 105.816 50.2741 105.771 50.2865L104.74 50.3797C104.594 50.3722 104.423 50.2754 104.386 50.1416C104.349 50.0078 104.446 49.837 104.568 49.7554Z" fill="#1E1E1E"/>
<path d="M96.2883 107.626L98.3879 106.612C98.5889 106.557 98.6338 106.328 98.606 106.227L98.3959 103.905C98.4129 103.576 98.6309 103.191 98.9324 103.107C99.2339 103.024 99.591 103.141 99.8028 103.516L100.817 105.615C100.872 105.816 101.101 105.861 101.202 105.833L103.524 105.623C103.853 105.64 104.238 105.858 104.322 106.16C104.405 106.461 104.288 106.818 103.913 107.03L101.814 108.044C101.613 108.1 101.568 108.328 101.596 108.429L101.806 110.751C101.789 111.08 101.571 111.465 101.269 111.549C100.968 111.632 100.611 111.515 100.399 111.141L99.385 109.041C99.3293 108.84 99.1005 108.795 99 108.823L96.6778 109.033C96.3485 109.016 95.9636 108.798 95.8801 108.497C95.7967 108.195 95.9142 107.838 96.2883 107.626Z" fill="#181818"/> <path d="M96.2885 107.626L98.3881 106.612C98.5891 106.557 98.6339 106.328 98.6061 106.227L98.396 103.905C98.413 103.576 98.631 103.191 98.9325 103.107C99.234 103.024 99.5911 103.141 99.8029 103.516L100.817 105.615C100.873 105.816 101.101 105.861 101.202 105.833L103.524 105.623C103.853 105.64 104.238 105.858 104.322 106.16C104.405 106.461 104.288 106.818 103.914 107.03L101.814 108.044C101.613 108.1 101.568 108.328 101.596 108.429L101.806 110.751C101.789 111.08 101.571 111.465 101.27 111.549C100.968 111.632 100.611 111.515 100.399 111.141L99.3851 109.041C99.3295 108.84 99.1006 108.795 99.0001 108.823L96.678 109.033C96.3487 109.016 95.9637 108.798 95.8802 108.497C95.7968 108.195 95.9143 107.838 96.2885 107.626Z" fill="#1E1E1E"/>
<path d="M106.127 86.1344L107.636 85.4056C107.781 85.3656 107.813 85.2012 107.793 85.129L107.642 83.4601C107.654 83.2234 107.811 82.9468 108.028 82.8868C108.244 82.8268 108.501 82.9112 108.653 83.1801L109.382 84.6891C109.422 84.8335 109.586 84.8658 109.659 84.8458L111.328 84.6948C111.564 84.707 111.841 84.8637 111.901 85.0804C111.961 85.297 111.876 85.5537 111.608 85.7059L110.099 86.4347C109.954 86.4747 109.922 86.6391 109.942 86.7114L110.093 88.3803C110.081 88.6169 109.924 88.8936 109.707 88.9536C109.491 89.0136 109.234 88.9291 109.082 88.6602L108.353 87.1512C108.313 87.0068 108.149 86.9746 108.076 86.9946L106.407 87.1456C106.171 87.1333 105.894 86.9766 105.834 86.76C105.774 86.5433 105.931 86.2666 106.127 86.1344Z" fill="#181818"/> <path d="M106.127 86.1344L107.636 85.4056C107.781 85.3656 107.813 85.2012 107.793 85.129L107.642 83.4601C107.654 83.2234 107.811 82.9468 108.028 82.8868C108.244 82.8268 108.501 82.9112 108.653 83.1801L109.382 84.6891C109.422 84.8335 109.586 84.8658 109.659 84.8458L111.327 84.6948C111.564 84.707 111.841 84.8637 111.901 85.0804C111.961 85.297 111.876 85.5537 111.607 85.7059L110.098 86.4347C109.954 86.4747 109.922 86.6391 109.942 86.7114L110.093 88.3803C110.081 88.6169 109.924 88.8936 109.707 88.9536C109.491 89.0136 109.234 88.9291 109.082 88.6602L108.353 87.1512C108.313 87.0068 108.148 86.9746 108.076 86.9946L106.407 87.1456C106.171 87.1333 105.894 86.9766 105.834 86.76C105.774 86.5433 105.931 86.2666 106.127 86.1344Z" fill="#1E1E1E"/>
<path d="M22.194 64.0334L24.4316 63.8309C24.6253 63.7773 24.7758 63.9442 24.8026 64.041L25.7797 66.0642C25.957 66.3279 26.3279 66.538 26.6184 66.4575C26.9089 66.3771 27.1458 66.103 27.1354 65.6888L26.933 63.4512C26.8794 63.2575 27.0462 63.1071 27.143 63.0803L29.1662 62.1031C29.4299 61.9259 29.64 61.5549 29.5596 61.2644C29.4791 60.9739 29.205 60.737 28.7909 60.7474L26.5532 60.9499C26.3596 61.0035 26.2091 60.8366 26.1823 60.7398L25.2052 58.7166C25.0279 58.4529 24.657 58.2428 24.3665 58.3233C24.0759 58.4037 23.8391 58.6778 23.8495 59.092L24.0519 61.3296C24.1055 61.5233 23.9387 61.6737 23.8418 61.7005L21.8187 62.6777C21.555 62.8549 21.3449 63.2259 21.4253 63.5164C21.5326 63.9037 21.9035 64.1138 22.194 64.0334Z" fill="#181818"/> <path d="M22.194 64.0334L24.4316 63.8309C24.6253 63.7773 24.7758 63.9442 24.8026 64.041L25.7797 66.0642C25.957 66.3279 26.3279 66.538 26.6184 66.4575C26.9089 66.3771 27.1458 66.103 27.1354 65.6888L26.933 63.4512C26.8794 63.2575 27.0462 63.1071 27.143 63.0803L29.1662 62.1031C29.4299 61.9259 29.64 61.5549 29.5596 61.2644C29.4791 60.9739 29.205 60.737 28.7909 60.7474L26.5532 60.9499C26.3596 61.0035 26.2091 60.8366 26.1823 60.7398L25.2052 58.7166C25.0279 58.4529 24.657 58.2428 24.3665 58.3233C24.0759 58.4037 23.8391 58.6778 23.8495 59.092L24.0519 61.3296C24.1055 61.5233 23.9387 61.6737 23.8418 61.7005L21.8187 62.6777C21.555 62.8549 21.3449 63.2259 21.4253 63.5164C21.5326 63.9037 21.9035 64.1138 22.194 64.0334Z" fill="#1E1E1E"/>
<path d="M10.2315 108.566L13.3953 108.28C13.6692 108.204 13.8819 108.44 13.9198 108.577L15.3014 111.438C15.552 111.811 16.0765 112.108 16.4872 111.994C16.898 111.88 17.2329 111.493 17.2182 110.907L16.932 107.743C16.8562 107.469 17.0921 107.257 17.229 107.219L20.0896 105.837C20.4624 105.587 20.7595 105.062 20.6458 104.651C20.532 104.241 20.1445 103.906 19.5589 103.92L16.3951 104.207C16.1212 104.282 15.9085 104.046 15.8706 103.91L14.489 101.049C14.2384 100.676 13.7139 100.379 13.3032 100.493C12.8924 100.606 12.5575 100.994 12.5722 101.58L12.8584 104.743C12.9342 105.017 12.6983 105.23 12.5614 105.268L9.7008 106.65C9.32795 106.9 9.03093 107.425 9.14465 107.835C9.29627 108.383 9.82073 108.68 10.2315 108.566Z" fill="#181818"/> <path d="M10.2317 108.566L13.3956 108.28C13.6694 108.204 13.8821 108.44 13.92 108.577L15.3016 111.438C15.5523 111.811 16.0767 112.108 16.4875 111.994C16.8982 111.88 17.2332 111.493 17.2185 110.907L16.9322 107.743C16.8564 107.469 17.0923 107.257 17.2292 107.219L20.0898 105.837C20.4627 105.587 20.7597 105.062 20.646 104.651C20.5323 104.241 20.1447 103.906 19.5592 103.92L16.3953 104.207C16.1215 104.282 15.9088 104.046 15.8709 103.91L14.4893 101.049C14.2386 100.676 13.7142 100.379 13.3034 100.493C12.8926 100.606 12.5577 100.994 12.5724 101.58L12.8587 104.743C12.9345 105.017 12.6986 105.23 12.5616 105.268L9.70104 106.65C9.32819 106.9 9.03118 107.425 9.14489 107.835C9.29652 108.383 9.82097 108.68 10.2317 108.566Z" fill="#1E1E1E"/>
<path d="M15.485 28.2522L18.4022 27.9883C18.6546 27.9184 18.8508 28.1359 18.8857 28.2622L20.1596 30.8997C20.3907 31.2435 20.8743 31.5173 21.253 31.4125C21.6317 31.3076 21.9406 30.9503 21.927 30.4104L21.6631 27.4932C21.5932 27.2408 21.8107 27.0446 21.9369 27.0097L24.5745 25.7358C24.9183 25.5047 25.1921 25.0211 25.0873 24.6424C24.9824 24.2637 24.6251 23.9548 24.0852 23.9684L21.168 24.2323C20.9155 24.3022 20.7194 24.0847 20.6845 23.9585L19.4106 21.3209C19.1795 20.9772 18.6959 20.7033 18.3172 20.8081C17.9385 20.913 17.6296 21.2703 17.6432 21.8102L17.9071 24.7274C17.977 24.9799 17.7595 25.176 17.6333 25.2109L14.9957 26.4848C14.6519 26.7159 14.3781 27.1995 14.4829 27.5782C14.6227 28.0832 15.1063 28.3571 15.485 28.2522Z" fill="#181818"/> <path d="M15.485 28.2522L18.4022 27.9883C18.6546 27.9184 18.8508 28.1359 18.8857 28.2622L20.1596 30.8997C20.3907 31.2435 20.8743 31.5173 21.253 31.4125C21.6317 31.3076 21.9406 30.9503 21.927 30.4104L21.6631 27.4932C21.5932 27.2408 21.8107 27.0446 21.9369 27.0097L24.5745 25.7358C24.9183 25.5047 25.1921 25.0211 25.0873 24.6424C24.9824 24.2637 24.6251 23.9548 24.0852 23.9684L21.168 24.2323C20.9155 24.3022 20.7194 24.0847 20.6845 23.9585L19.4106 21.3209C19.1795 20.9772 18.6959 20.7033 18.3172 20.8081C17.9385 20.913 17.6296 21.2703 17.6432 21.8102L17.9071 24.7274C17.977 24.9799 17.7595 25.176 17.6333 25.2109L14.9957 26.4848C14.6519 26.7159 14.3781 27.1995 14.4829 27.5782C14.6227 28.0832 15.1063 28.3571 15.485 28.2522Z" fill="#1E1E1E"/>
<path d="M131.048 82.8904L135.025 84.3075C135.397 84.3686 135.522 84.7717 135.492 84.958L135.565 89.1786C135.66 89.768 136.127 90.4186 136.686 90.5102C137.245 90.6018 137.865 90.3208 138.173 89.6062L139.59 85.6299C139.651 85.2573 140.055 85.1321 140.241 85.1627L144.462 85.0895C145.051 84.9948 145.701 84.5275 145.793 83.9687C145.885 83.4098 145.604 82.7898 144.889 82.4813L140.913 81.0642C140.54 81.0031 140.415 80.6 140.446 80.4137L140.372 76.1931C140.278 75.6037 139.81 74.9532 139.252 74.8615C138.693 74.7699 138.073 75.0509 137.764 75.7655L136.347 79.7418C136.286 80.1144 135.883 80.2396 135.697 80.209L131.476 80.2823C130.887 80.3769 130.236 80.8442 130.144 81.4031C130.022 82.1483 130.489 82.7988 131.048 82.8904Z" fill="#181818"/> <path d="M131.048 82.8904L135.025 84.3075C135.397 84.3686 135.523 84.7717 135.492 84.958L135.565 89.1786C135.66 89.768 136.127 90.4186 136.686 90.5102C137.245 90.6018 137.865 90.3208 138.173 89.6062L139.59 85.6299C139.652 85.2573 140.055 85.1321 140.241 85.1627L144.462 85.0895C145.051 84.9948 145.702 84.5275 145.793 83.9687C145.885 83.4098 145.604 82.7898 144.889 82.4813L140.913 81.0642C140.54 81.0031 140.415 80.6 140.446 80.4137L140.372 76.1931C140.278 75.6037 139.811 74.9532 139.252 74.8615C138.693 74.7699 138.073 75.0509 137.764 75.7655L136.347 79.7418C136.286 80.1144 135.883 80.2396 135.697 80.209L131.476 80.2823C130.887 80.3769 130.236 80.8442 130.145 81.4031C130.022 82.1483 130.49 82.7988 131.048 82.8904Z" fill="#1E1E1E"/>
<path d="M47.654 42.7536C47.2846 42.8559 47.068 43.2383 47.1703 43.6077C47.2726 43.9771 47.655 44.1937 48.0244 44.0914C48.3939 43.9892 48.6104 43.6068 48.5082 43.2373C48.4059 42.8679 48.0235 42.6513 47.654 42.7536Z" fill="#181818"/> <path d="M47.6538 42.7536C47.2844 42.8559 47.0678 43.2383 47.1701 43.6077C47.2723 43.9771 47.6547 44.1937 48.0242 44.0914C48.3936 43.9892 48.6102 43.6068 48.5079 43.2373C48.4056 42.8679 48.0232 42.6513 47.6538 42.7536Z" fill="#1E1E1E"/>
<path d="M71.5437 60.1943C71.1743 60.2965 70.9577 60.6789 71.06 61.0484C71.1622 61.4178 71.5446 61.6344 71.9141 61.5321C72.2835 61.4298 72.5001 61.0474 72.3978 60.678C72.2955 60.3086 71.9131 60.092 71.5437 60.1943Z" fill="#181818"/> <path d="M71.5437 60.1943C71.1743 60.2965 70.9577 60.6789 71.06 61.0484C71.1622 61.4178 71.5446 61.6344 71.9141 61.5321C72.2835 61.4298 72.5001 61.0474 72.3978 60.678C72.2955 60.3086 71.9131 60.092 71.5437 60.1943Z" fill="#1E1E1E"/>
<path d="M87.0624 32.0586C86.8667 32.3882 86.9753 32.8141 87.3049 33.0098C87.6345 33.2054 88.0603 33.0969 88.256 32.7672C88.4517 32.4376 88.3431 32.0118 88.0135 31.8161C87.6839 31.6204 87.2581 31.729 87.0624 32.0586Z" fill="#181818"/> <path d="M87.0625 32.0586C86.8668 32.3882 86.9754 32.8141 87.305 33.0098C87.6346 33.2054 88.0605 33.0969 88.2562 32.7672C88.4518 32.4376 88.3433 32.0118 88.0136 31.8161C87.684 31.6204 87.2582 31.729 87.0625 32.0586Z" fill="#1E1E1E"/>
<path d="M64.6673 18.7561C64.4717 19.0857 64.5802 19.5116 64.9099 19.7073C65.2395 19.9029 65.6653 19.7944 65.861 19.4648C66.0567 19.1351 65.9481 18.7093 65.6185 18.5136C65.2889 18.3179 64.863 18.4265 64.6673 18.7561Z" fill="#181818"/> <path d="M64.6673 18.7561C64.4717 19.0857 64.5802 19.5116 64.9099 19.7073C65.2395 19.9029 65.6653 19.7944 65.861 19.4648C66.0567 19.1351 65.9481 18.7093 65.6185 18.5136C65.2889 18.3179 64.863 18.4265 64.6673 18.7561Z" fill="#1E1E1E"/>
<path d="M24.7157 49.313C24.52 49.6426 24.6286 50.0685 24.9582 50.2641C25.2878 50.4598 25.7137 50.3513 25.9094 50.0216C26.105 49.692 25.9965 49.2662 25.6668 49.0705C25.3372 48.8748 24.9114 48.9834 24.7157 49.313Z" fill="#181818"/> <path d="M24.7159 49.313C24.5202 49.6426 24.6288 50.0685 24.9584 50.2641C25.2881 50.4598 25.7139 50.3513 25.9096 50.0216C26.1053 49.692 25.9967 49.2662 25.6671 49.0705C25.3375 48.8748 24.9116 48.9834 24.7159 49.313Z" fill="#1E1E1E"/>
<path d="M27.6244 76.6072C27.4287 76.9368 27.5373 77.3627 27.8669 77.5583C28.1965 77.754 28.6224 77.6454 28.818 77.3158C29.0137 76.9862 28.9052 76.5604 28.5755 76.3647C28.2459 76.169 27.8201 76.2776 27.6244 76.6072Z" fill="#181818"/> <path d="M27.6241 76.6072C27.4285 76.9368 27.537 77.3627 27.8666 77.5583C28.1963 77.754 28.6221 77.6454 28.8178 77.3158C29.0135 76.9862 28.9049 76.5604 28.5753 76.3647C28.2457 76.169 27.8198 76.2776 27.6241 76.6072Z" fill="#1E1E1E"/>
<path d="M35.1957 103.887C35 104.217 35.1086 104.642 35.4382 104.838C35.7678 105.034 36.1936 104.925 36.3893 104.596C36.585 104.266 36.4764 103.84 36.1468 103.644C35.8172 103.449 35.3914 103.557 35.1957 103.887Z" fill="#181818"/> <path d="M35.1957 103.887C35 104.217 35.1086 104.642 35.4382 104.838C35.7678 105.034 36.1936 104.925 36.3893 104.596C36.585 104.266 36.4764 103.84 36.1468 103.644C35.8172 103.449 35.3914 103.557 35.1957 103.887Z" fill="#1E1E1E"/>
<path d="M53.5052 117.827C53.3096 118.157 53.4181 118.583 53.7478 118.779C54.0774 118.974 54.5032 118.866 54.6989 118.536C54.8946 118.206 54.786 117.781 54.4564 117.585C54.1268 117.389 53.7009 117.498 53.5052 117.827Z" fill="#181818"/> <path d="M53.5051 117.827C53.3094 118.157 53.418 118.583 53.7476 118.779C54.0773 118.974 54.5031 118.866 54.6988 118.536C54.8945 118.206 54.7859 117.781 54.4563 117.585C54.1266 117.389 53.7008 117.498 53.5051 117.827Z" fill="#1E1E1E"/>
<path d="M86.9999 118.051C86.8042 118.381 86.9128 118.807 87.2424 119.002C87.572 119.198 87.9978 119.089 88.1935 118.76C88.3892 118.43 88.2806 118.004 87.951 117.809C87.6214 117.613 87.1956 117.721 86.9999 118.051Z" fill="#181818"/> <path d="M87 118.051C86.8043 118.381 86.9129 118.807 87.2425 119.002C87.5721 119.198 87.998 119.089 88.1937 118.76C88.3893 118.43 88.2808 118.004 87.9511 117.809C87.6215 117.613 87.1957 117.721 87 118.051Z" fill="#1E1E1E"/>
<path d="M110.305 97.2576C110.109 97.5872 110.217 98.013 110.547 98.2087C110.877 98.4044 111.303 98.2958 111.498 97.9662C111.694 97.6366 111.585 97.2108 111.256 97.0151C110.926 96.8194 110.5 96.928 110.305 97.2576Z" fill="#181818"/> <path d="M110.305 97.2576C110.109 97.5872 110.218 98.013 110.547 98.2087C110.877 98.4044 111.303 98.2958 111.498 97.9662C111.694 97.6366 111.585 97.2108 111.256 97.0151C110.926 96.8194 110.5 96.928 110.305 97.2576Z" fill="#1E1E1E"/>
<path d="M122.725 74.7053C122.53 75.035 122.638 75.4608 122.968 75.6565C123.298 75.8522 123.723 75.7436 123.919 75.414C124.115 75.0843 124.006 74.6585 123.677 74.4628C123.347 74.2671 122.921 74.3757 122.725 74.7053Z" fill="#181818"/> <path d="M122.725 74.7053C122.53 75.035 122.638 75.4608 122.968 75.6565C123.298 75.8522 123.723 75.7436 123.919 75.414C124.115 75.0843 124.006 74.6585 123.677 74.4628C123.347 74.2671 122.921 74.3757 122.725 74.7053Z" fill="#1E1E1E"/>
<path d="M108.7 40.0772C108.504 40.4068 108.613 40.8326 108.943 41.0283C109.272 41.224 109.698 41.1154 109.894 40.7858C110.089 40.4562 109.981 40.0303 109.651 39.8346C109.322 39.639 108.896 39.7475 108.7 40.0772Z" fill="#181818"/> <path d="M108.7 40.0772C108.505 40.4068 108.613 40.8326 108.943 41.0283C109.272 41.224 109.698 41.1154 109.894 40.7858C110.09 40.4562 109.981 40.0303 109.651 39.8346C109.322 39.639 108.896 39.7475 108.7 40.0772Z" fill="#1E1E1E"/>
<path d="M106.348 78.3655L112.645 73.4041L110.728 70.8971L104.431 75.8585L106.348 78.3655Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M106.348 78.3655L112.645 73.4041L110.729 70.8971L104.431 75.8585L106.348 78.3655Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M146.37 58.2505L134.023 42.0017L104.475 65.4607L116.823 81.7095L146.37 58.2505Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M146.37 58.2505L134.023 42.0017L104.475 65.4607L116.823 81.7095L146.37 58.2505Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M124.767 75.0697L112.405 58.7385" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M124.767 75.0697L112.405 58.7385" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M131.697 69.8236L119.335 53.4924" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M131.697 69.8236L119.335 53.4924" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M138.628 64.5775L126.266 48.2463" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M138.628 64.5775L126.266 48.2463" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M138.32 48.4403L108.206 71.2356" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M138.321 48.4403L108.206 71.2356" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M143.052 54.6918L112.938 77.4871" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M143.053 54.6918L112.938 77.4871" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M113.433 102.184C111.233 103.85 108.107 103.414 106.438 101.209L103.672 97.5551C103.535 97.3739 103.572 97.1089 103.752 96.972L111.075 91.4293C111.256 91.2924 111.521 91.3294 111.658 91.5106L114.424 95.1646C116.062 97.3919 115.603 100.542 113.433 102.184Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M113.433 102.184C111.233 103.85 108.107 103.414 106.438 101.209L103.672 97.5551C103.535 97.3739 103.571 97.1089 103.752 96.972L111.075 91.4293C111.255 91.2924 111.52 91.3294 111.658 91.5106L114.423 95.1646C116.062 97.3919 115.603 100.542 113.433 102.184Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M90.6043 86.1563L84.3071 91.1177L86.2238 93.6246L92.521 88.6632L90.6043 86.1563Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M90.6046 86.1563L84.3074 91.1177L86.224 93.6246L92.5212 88.6632L90.6046 86.1563Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M62.943 122.491L50.5952 106.242L80.1426 82.7835L92.4905 99.0323L62.943 122.491Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M62.9433 122.491L50.5955 106.242L80.1429 82.7835L92.4907 99.0323L62.9433 122.491Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M83.7494 106.361L71.3877 90.0295" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M83.7494 106.361L71.3877 90.0295" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M76.8183 111.607L64.4565 95.2756" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M76.8187 111.607L64.4569 95.2756" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M69.8881 116.853L57.5264 100.522" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M69.8882 116.853L57.5265 100.522" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M54.5472 112.295L84.2392 88.6205" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M54.5474 112.295L84.2394 88.6205" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M59.2791 118.546L88.9711 94.8715" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M59.2794 118.546L88.9714 94.8715" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M96.117 64.6554L81.9844 75.353L101.414 101.022L101.437 101.052C101.437 101.052 101.437 101.052 101.46 101.082C102.267 102.086 106.048 100.507 109.936 97.5641C113.823 94.6217 116.34 91.4342 115.623 90.3618C115.623 90.3618 115.623 90.3618 115.6 90.3316L115.577 90.3014L96.1472 64.6326L96.117 64.6554Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M96.117 64.6554L81.9844 75.353L101.414 101.022L101.437 101.052C101.437 101.052 101.437 101.052 101.46 101.082C102.267 102.086 106.048 100.507 109.936 97.5641C113.823 94.6217 116.34 91.4342 115.623 90.3618C115.623 90.3618 115.623 90.3618 115.6 90.3316L115.577 90.3014L96.1472 64.6326L96.117 64.6554Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M103.089 73.8662L108.484 80.9931L108.507 81.0233L108.529 81.0535C109.276 82.1031 106.73 85.3134 102.842 88.2558C98.9552 91.1983 95.2041 92.7551 94.3666 91.7739L94.3438 91.7437L94.3209 91.7135L88.9263 84.5867C89.7336 85.5906 93.5148 84.011 97.402 81.0686C101.289 78.1262 103.813 74.8856 103.089 73.8662Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M103.089 73.8662L108.484 80.9931L108.506 81.0233L108.529 81.0535C109.276 82.1031 106.73 85.3134 102.842 88.2558C98.955 91.1983 95.204 92.7551 94.3665 91.7739L94.3436 91.7437L94.3208 91.7135L88.9261 84.5867C89.7335 85.5906 93.5147 84.011 97.4019 81.0686C101.289 78.1262 103.813 74.8856 103.089 73.8662Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M97.3668 52.3623C100.626 56.6245 97.1992 64.8705 89.702 70.7763C82.2047 76.6822 73.4525 78.0302 70.1932 73.768C70.1735 73.7421 70.1537 73.7163 70.1339 73.6905C70.1142 73.6646 70.0747 73.613 70.0549 73.5871C69.0398 71.8619 74.1021 65.8873 81.4728 60.0811C88.8436 54.275 95.8219 50.8061 97.1969 52.2064C97.2166 52.2323 97.2562 52.2839 97.2759 52.3098C97.3075 52.2848 97.3273 52.3107 97.3668 52.3623Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M97.3669 52.3623C100.626 56.6245 97.1994 64.8705 89.7021 70.7763C82.2048 76.6822 73.4526 78.0302 70.1933 73.768C70.1736 73.7421 70.1538 73.7163 70.1341 73.6905C70.1143 73.6646 70.0748 73.613 70.0551 73.5871C69.0399 71.8619 74.1022 65.8873 81.473 60.0811C88.8437 54.275 95.822 50.8061 97.197 52.2064C97.2168 52.2323 97.2563 52.2839 97.276 52.3098C97.3077 52.2848 97.3274 52.3107 97.3669 52.3623Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M96.7129 52.14L96.7323 52.1651C97.089 52.69 96.7938 53.7221 95.9555 55.0165C94.2672 57.6545 90.4412 61.5148 85.5679 65.3075C80.6946 69.1002 75.9845 71.8831 73.0478 72.8446C71.5892 73.3378 70.5185 73.3686 70.0998 72.892L70.0804 72.867C70.061 72.842 70.0416 72.8169 70.0222 72.7919C69.0259 71.1198 73.9931 65.3282 81.2254 59.6995C88.4578 54.0709 95.3054 50.7077 96.6548 52.0649C96.6742 52.09 96.6935 52.115 96.7129 52.14Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M96.7131 52.14L96.7324 52.1651C97.0891 52.69 96.794 53.7221 95.9556 55.0165C94.2674 57.6545 90.4413 61.5148 85.568 65.3075C80.6947 69.1002 75.9847 71.8831 73.0479 72.8446C71.5893 73.3378 70.5187 73.3686 70.0999 72.892L70.0805 72.867C70.0611 72.842 70.0418 72.8169 70.0224 72.7919C69.0261 71.1198 73.9932 65.3282 81.2256 59.6995C88.4579 54.0709 95.3055 50.7077 96.6549 52.0649C96.6743 52.09 96.6937 52.115 96.7131 52.14Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M95.9967 55.2585L96.0159 55.2833C94.3369 57.8956 90.5319 61.7185 85.6852 65.4745C80.8386 69.2304 76.1543 71.9864 73.2336 72.9386L73.2143 72.9139C73.195 72.8891 73.1757 72.8643 73.1757 72.8643C72.3274 71.4555 76.5804 66.4909 82.7545 61.7062C88.9594 56.8977 94.8011 54.0394 95.9581 55.2089C95.9581 55.2089 95.9774 55.2337 95.9967 55.2585Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M95.9965 55.2585L96.0158 55.2833C94.3368 57.8956 90.5318 61.7185 85.6851 65.4745C80.8385 69.2304 76.1541 71.9864 73.2334 72.9386L73.2142 72.9139C73.1949 72.8891 73.1756 72.8643 73.1756 72.8643C72.3273 71.4555 76.5803 66.4909 82.7543 61.7062C88.9593 56.8977 94.801 54.0394 95.958 55.2089C95.958 55.2089 95.9773 55.2337 95.9965 55.2585Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M84.8829 61.2183C84.9771 61.4173 84.3981 62.1113 83.5747 62.7728C82.7169 63.4618 81.9237 63.8702 81.7737 63.6702L77.0693 56.0049L78.6818 54.7096L84.84 61.1612L84.8829 61.2183Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M84.8829 61.2183C84.9771 61.4173 84.3981 62.1113 83.5747 62.7728C82.7169 63.4618 81.9237 63.8702 81.7737 63.6702L77.0693 56.0049L78.6818 54.7096L84.84 61.1612L84.8829 61.2183Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M78.2283 55.6796C79.2373 54.8746 79.5446 53.5456 78.9146 52.7111C78.2846 51.8766 76.956 51.8526 75.947 52.6576C74.9381 53.4625 74.6308 54.7915 75.2608 55.626C75.8907 56.4605 77.2194 56.4845 78.2283 55.6796Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10"/> <path d="M78.2285 55.6796C79.2374 54.8746 79.5447 53.5456 78.9147 52.7111C78.2847 51.8766 76.9561 51.8526 75.9472 52.6576C74.9382 53.4625 74.6309 54.7915 75.2609 55.626C75.8909 56.4605 77.2195 56.4845 78.2285 55.6796Z" fill="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
<path d="M36.2208 32.2775C35.9844 33.8534 34.9601 36.9262 33.148 37.399C33.0692 37.399 32.9904 37.399 32.9116 37.4778C31.1782 37.7141 29.2084 36.5323 27.9477 35.6656C27.3174 35.1928 26.6871 34.6413 26.1355 34.0897C25.8992 33.8534 25.584 33.5382 25.3476 33.3018C23.5354 31.332 22.1172 28.8895 21.2505 26.2894C21.0929 25.7379 20.9353 25.1863 21.0141 24.6348C21.0141 24.4772 21.0929 24.3984 21.0929 24.3196C21.2505 23.9257 21.5657 23.5317 22.0384 23.4529C22.4324 23.3741 22.7475 23.5317 23.1415 23.6105C23.5354 23.7681 23.8506 23.9257 24.2446 24.1621C23.3779 23.1378 23.1415 21.4044 23.1415 20.1437C23.1415 20.0649 23.1415 19.9073 23.2203 19.9073C23.2991 19.8285 23.5354 19.9073 23.6142 19.9861C24.8749 20.9316 24.8749 22.665 25.4264 24.0833C25.4264 23.059 25.5052 22.0347 25.5052 21.0104C25.5052 20.3013 25.584 19.4346 26.1355 18.9618C26.2143 18.8831 26.2931 18.8831 26.3719 18.8831C26.5295 18.8831 26.6083 19.0406 26.6871 19.1194C27.6326 20.774 27.081 22.8226 27.2386 24.7136C27.3174 25.4227 27.3962 26.1318 27.5538 26.841C27.6326 27.1561 27.7902 27.8652 27.9477 28.5744C28.1053 28.8895 28.2629 29.2047 28.4993 29.5199C28.5781 29.6774 28.7357 29.835 28.8932 29.9138C29.1296 30.1502 29.4448 30.3866 29.7599 30.6229C30.3903 30.9381 31.0206 31.1745 31.7297 31.332C32.9904 31.5684 34.251 31.5684 35.5117 31.6472C35.7481 31.6472 35.9844 31.726 36.142 31.8836C36.2208 32.0412 36.2208 32.12 36.2208 32.2775Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M36.2208 32.2775C35.9844 33.8534 34.9601 36.9262 33.148 37.399C33.0692 37.399 32.9904 37.399 32.9116 37.4778C31.1782 37.7141 29.2084 36.5323 27.9477 35.6656C27.3174 35.1928 26.6871 34.6413 26.1355 34.0897C25.8992 33.8534 25.584 33.5382 25.3476 33.3018C23.5354 31.332 22.1172 28.8895 21.2505 26.2894C21.0929 25.7379 20.9353 25.1863 21.0141 24.6348C21.0141 24.4772 21.0929 24.3984 21.0929 24.3196C21.2505 23.9257 21.5657 23.5317 22.0384 23.4529C22.4324 23.3741 22.7475 23.5317 23.1415 23.6105C23.5354 23.7681 23.8506 23.9257 24.2446 24.1621C23.3779 23.1378 23.1415 21.4044 23.1415 20.1437C23.1415 20.0649 23.1415 19.9073 23.2203 19.9073C23.2991 19.8285 23.5354 19.9073 23.6142 19.9861C24.8749 20.9316 24.8749 22.665 25.4264 24.0833C25.4264 23.059 25.5052 22.0347 25.5052 21.0104C25.5052 20.3013 25.584 19.4346 26.1355 18.9618C26.2143 18.8831 26.2931 18.8831 26.3719 18.8831C26.5295 18.8831 26.6083 19.0406 26.6871 19.1194C27.6326 20.774 27.081 22.8226 27.2386 24.7136C27.3174 25.4227 27.3962 26.1318 27.5538 26.841C27.6326 27.1561 27.7902 27.8652 27.9477 28.5744C28.1053 28.8895 28.2629 29.2047 28.4993 29.5199C28.5781 29.6774 28.7357 29.835 28.8932 29.9138C29.1296 30.1502 29.4448 30.3866 29.7599 30.6229C30.3903 30.9381 31.0206 31.1745 31.7297 31.332C32.9904 31.5684 34.251 31.5684 35.5117 31.6472C35.7481 31.6472 35.9844 31.726 36.142 31.8836C36.2208 32.0412 36.2208 32.12 36.2208 32.2775Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M52.7667 58.3572C51.1121 58.3572 49.3787 57.8056 48.118 57.0177C45.6755 55.4419 44.1785 53.0781 42.7603 50.9508L45.5179 49.1386C46.7786 51.0296 48.0393 53.0781 49.8515 54.1812C51.2697 55.1267 53.5546 55.4419 54.8153 54.3388C56.3911 52.9206 55.9972 50.2417 55.3668 48.2719C54.5789 45.8294 53.0819 43.702 51.2697 42.3626L53.2395 39.7625C55.682 41.5746 57.573 44.2535 58.5185 47.3264C59.8579 51.5023 59.3852 54.8116 57.0214 56.8601C55.8396 57.8844 54.2638 58.3572 52.7667 58.3572Z" fill="#525252"/> <path d="M52.7665 58.3572C51.1119 58.3572 49.3785 57.8056 48.1178 57.0177C45.6753 55.4419 44.1782 53.0781 42.76 50.9508L45.5177 49.1386C46.7784 51.0296 48.039 53.0781 49.8512 54.1812C51.2695 55.1267 53.5544 55.4419 54.815 54.3388C56.3909 52.9206 55.9969 50.2417 55.3666 48.2719C54.5787 45.8294 53.0816 43.702 51.2694 42.3626L53.2392 39.7625C55.6818 41.5746 57.5727 44.2535 58.5182 47.3264C59.8577 51.5023 59.3849 54.8116 57.0212 56.8601C55.8393 57.8844 54.2635 58.3572 52.7665 58.3572Z" fill="#575757"/>
<path d="M56.155 32.1987C56.2338 33.3806 55.8399 34.5625 55.3671 35.7443C53.5549 40.4718 51.6639 45.1992 49.4578 49.7691C48.985 50.6358 48.5911 51.5813 48.1183 52.448C47.488 53.7087 46.7001 55.1269 45.2819 55.4421C44.1 55.6785 42.9969 55.1269 41.9726 54.5754C41.6575 54.4178 41.3423 54.1814 41.1847 53.8663C41.0271 53.5511 41.1059 53.1571 41.1847 52.842C42.1302 48.1145 43.9424 43.7022 45.7546 39.2111C46.6213 37.005 47.5668 34.72 48.2759 32.4351C48.8275 30.8593 49.2214 28.8107 51.27 28.6531C53.161 28.5743 55.9975 30.0714 56.155 32.1987Z" fill="#525252"/> <path d="M56.155 32.1987C56.2338 33.3806 55.8399 34.5625 55.3671 35.7443C53.5549 40.4718 51.6639 45.1992 49.4578 49.7691C48.985 50.6358 48.5911 51.5813 48.1183 52.448C47.488 53.7087 46.7001 55.1269 45.2819 55.4421C44.1 55.6785 42.9969 55.1269 41.9726 54.5754C41.6575 54.4178 41.3423 54.1814 41.1847 53.8663C41.0271 53.5511 41.1059 53.1571 41.1847 52.842C42.1302 48.1145 43.9424 43.7022 45.7546 39.2111C46.6213 37.005 47.5668 34.72 48.2759 32.4351C48.8275 30.8593 49.2214 28.8107 51.27 28.6531C53.161 28.5743 55.9975 30.0714 56.155 32.1987Z" fill="#575757"/>
<path d="M34.1721 46.3021C34.1721 46.4596 34.1721 46.5384 34.1721 46.696C34.0933 47.7203 33.8569 48.6658 33.3054 49.5325C32.6751 50.478 31.7296 51.2659 30.6265 51.5023C30.3901 51.5811 30.1538 51.5811 29.9174 51.5811C28.8931 51.6599 27.8688 51.3447 26.9233 50.9507C25.7415 50.478 24.5596 49.9264 23.2201 49.8477C22.511 49.7689 21.8019 49.8477 21.0928 50.0052C20.6988 50.084 20.3837 50.2416 19.9897 50.3204C19.5957 50.478 19.2018 50.6356 18.8078 50.7932C17.4684 51.4235 16.1289 52.2902 14.7895 52.8417C13.6864 53.3145 12.347 53.5508 11.4015 52.8417C10.9287 52.5266 10.6924 51.975 10.456 51.5023C9.11653 48.981 7.93466 46.4596 6.7528 43.8595C6.67401 43.7807 6.59521 43.6232 6.59521 43.4656C6.59521 43.0716 7.06796 42.7565 7.38313 42.5989C7.46192 42.5201 7.54071 42.5201 7.6195 42.4413C8.4862 42.0473 9.27411 41.7322 10.2196 41.5746C11.2439 41.4958 12.347 41.6534 13.0561 42.3625C13.5288 42.8352 13.844 43.3868 14.1592 44.0171C14.4743 44.7262 14.8683 45.4354 15.1834 46.1445C16.1289 45.3566 17.0744 44.5687 18.0987 43.8595C18.4139 43.6232 18.729 43.4656 19.0442 43.308C19.5957 42.9928 20.2261 42.6777 20.8564 42.5201C21.8807 42.1261 22.9838 41.9685 24.0868 41.8898C24.6384 41.811 25.1899 41.7322 25.7415 41.811C26.3718 41.811 27.0021 41.9685 27.5536 42.0473C29.0507 42.3625 30.6265 42.6777 32.1235 42.9928L30.4689 42.8353C31.8084 43.1504 33.3842 43.6232 33.9357 44.8838C34.1721 45.2778 34.1721 45.8293 34.1721 46.3021Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M34.1721 46.3021C34.1721 46.4596 34.1721 46.5384 34.1721 46.696C34.0933 47.7203 33.8569 48.6658 33.3054 49.5325C32.6751 50.478 31.7296 51.2659 30.6265 51.5023C30.3901 51.5811 30.1538 51.5811 29.9174 51.5811C28.8931 51.6599 27.8688 51.3447 26.9233 50.9507C25.7415 50.478 24.5596 49.9264 23.2201 49.8477C22.511 49.7689 21.8019 49.8477 21.0928 50.0052C20.6988 50.084 20.3837 50.2416 19.9897 50.3204C19.5957 50.478 19.2018 50.6356 18.8078 50.7932C17.4684 51.4235 16.1289 52.2902 14.7895 52.8417C13.6864 53.3145 12.347 53.5508 11.4015 52.8417C10.9287 52.5266 10.6924 51.975 10.456 51.5023C9.11653 48.981 7.93466 46.4596 6.7528 43.8595C6.67401 43.7807 6.59521 43.6232 6.59521 43.4656C6.59521 43.0716 7.06796 42.7565 7.38313 42.5989C7.46192 42.5201 7.54071 42.5201 7.6195 42.4413C8.4862 42.0473 9.27411 41.7322 10.2196 41.5746C11.2439 41.4958 12.347 41.6534 13.0561 42.3625C13.5288 42.8352 13.844 43.3868 14.1592 44.0171C14.4743 44.7262 14.8683 45.4354 15.1834 46.1445C16.1289 45.3566 17.0744 44.5687 18.0987 43.8595C18.4139 43.6232 18.729 43.4656 19.0442 43.308C19.5957 42.9928 20.2261 42.6777 20.8564 42.5201C21.8807 42.1261 22.9838 41.9685 24.0868 41.8898C24.6384 41.811 25.1899 41.7322 25.7415 41.811C26.3718 41.811 27.0021 41.9685 27.5536 42.0473C29.0507 42.3625 30.6265 42.6777 32.1235 42.9928L30.4689 42.8353C31.8084 43.1504 33.3842 43.6232 33.9357 44.8838C34.1721 45.2778 34.1721 45.8293 34.1721 46.3021Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28.3416 42.5203C27.7901 44.6476 28.2628 46.9326 29.6023 48.7448C29.6811 48.8236 29.7599 48.9811 29.9175 48.9024C29.9962 48.9024 29.9962 48.8236 30.075 48.7448C30.3114 48.1932 30.3902 47.5629 30.469 47.0114C30.6266 45.6719 31.0993 43.5446 30.6266 42.2051C30.075 40.9445 28.578 41.496 28.3416 42.5203Z" fill="#525252"/> <path d="M28.3414 42.5203C27.7899 44.6476 28.2626 46.9326 29.602 48.7448C29.6808 48.8236 29.7596 48.9811 29.9172 48.9024C29.996 48.9024 29.996 48.8236 30.0748 48.7448C30.3112 48.1932 30.39 47.5629 30.4687 47.0114C30.6263 45.6719 31.0991 43.5446 30.6263 42.2051C30.0748 40.9445 28.5778 41.496 28.3414 42.5203Z" fill="#575757"/>
<path d="M38.1115 27.3921C37.4812 28.2588 36.6933 28.9679 35.9842 29.7558C31.8082 34.2469 29.8385 40.4714 28.6566 46.5383C28.3414 48.1141 28.1839 49.8475 29.1294 51.0294C29.6021 51.5809 30.2324 51.9749 30.8628 52.2901C31.887 52.8416 32.8325 53.3931 33.8568 53.9447C35.0387 54.575 36.2993 55.2841 37.6388 55.3629C39.2146 55.4417 40.7904 54.6538 41.9723 53.5507C43.1542 52.4476 43.9421 51.0294 44.73 49.6112C46.6998 45.9868 48.512 42.2836 50.0878 38.5016C50.5605 37.3985 51.0333 36.2167 51.1909 35.0348C51.4272 33.3014 51.0333 31.568 50.7181 29.9134C50.6393 29.5194 50.5605 29.0467 50.2454 28.7315C49.8514 28.3376 49.2211 28.3376 48.7483 28.4164C45.2815 28.5739 41.7359 28.8891 38.4267 28.1012C37.7176 27.9436 36.9297 27.7072 36.5357 27.0769" fill="#181818"/> <path d="M38.1115 27.3921C37.4812 28.2588 36.6933 28.9679 35.9842 29.7558C31.8082 34.2469 29.8385 40.4714 28.6566 46.5383C28.3414 48.1141 28.1839 49.8475 29.1294 51.0294C29.6021 51.5809 30.2324 51.9749 30.8628 52.2901C31.887 52.8416 32.8325 53.3931 33.8568 53.9447C35.0387 54.575 36.2993 55.2841 37.6388 55.3629C39.2146 55.4417 40.7904 54.6538 41.9723 53.5507C43.1542 52.4476 43.9421 51.0294 44.73 49.6112C46.6998 45.9868 48.512 42.2836 50.0878 38.5016C50.5605 37.3985 51.0333 36.2167 51.1909 35.0348C51.4272 33.3014 51.0333 31.568 50.7181 29.9134C50.6393 29.5194 50.5605 29.0467 50.2454 28.7315C49.8514 28.3376 49.2211 28.3376 48.7483 28.4164C45.2815 28.5739 41.7359 28.8891 38.4267 28.1012C37.7176 27.9436 36.9297 27.7072 36.5357 27.0769" fill="#1E1E1E"/>
<path d="M38.1115 27.3921C37.4812 28.2588 36.6933 28.9679 35.9842 29.7558C31.8082 34.2469 29.8385 40.4714 28.6566 46.5383C28.3414 48.1141 28.1839 49.8475 29.1294 51.0294C29.6021 51.5809 30.2324 51.9749 30.8628 52.2901C31.887 52.8416 32.8325 53.3931 33.8568 53.9447C35.0387 54.575 36.2993 55.2841 37.6388 55.3629C39.2146 55.4417 40.7904 54.6538 41.9723 53.5507C43.1542 52.4476 43.9421 51.0294 44.73 49.6112C46.6998 45.9868 48.512 42.2836 50.0878 38.5016C50.5605 37.3985 51.0333 36.2167 51.1909 35.0348C51.4272 33.3014 51.0333 31.568 50.7181 29.9134C50.6393 29.5194 50.5605 29.0467 50.2454 28.7315C49.8514 28.3376 49.2211 28.3376 48.7483 28.4164C45.2815 28.5739 41.7359 28.8891 38.4267 28.1012C37.7176 27.9436 36.9297 27.7072 36.5357 27.0769" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M38.1115 27.3921C37.4812 28.2588 36.6933 28.9679 35.9842 29.7558C31.8082 34.2469 29.8385 40.4714 28.6566 46.5383C28.3414 48.1141 28.1839 49.8475 29.1294 51.0294C29.6021 51.5809 30.2324 51.9749 30.8628 52.2901C31.887 52.8416 32.8325 53.3931 33.8568 53.9447C35.0387 54.575 36.2993 55.2841 37.6388 55.3629C39.2146 55.4417 40.7904 54.6538 41.9723 53.5507C43.1542 52.4476 43.9421 51.0294 44.73 49.6112C46.6998 45.9868 48.512 42.2836 50.0878 38.5016C50.5605 37.3985 51.0333 36.2167 51.1909 35.0348C51.4272 33.3014 51.0333 31.568 50.7181 29.9134C50.6393 29.5194 50.5605 29.0467 50.2454 28.7315C49.8514 28.3376 49.2211 28.3376 48.7483 28.4164C45.2815 28.5739 41.7359 28.8891 38.4267 28.1012C37.7176 27.9436 36.9297 27.7072 36.5357 27.0769" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.0927 50.084C20.6987 50.1628 20.3836 50.3203 19.9896 50.3991C19.9108 49.2961 19.7532 48.0354 19.4381 46.8535C19.1229 45.8292 18.729 44.8838 18.0986 43.9383C18.4138 43.7019 18.729 43.5443 19.0441 43.3867C19.6745 44.411 20.1472 45.4353 20.4624 46.5384C20.7775 47.7202 21.0139 48.9809 21.0927 50.084Z" fill="#525252"/> <path d="M21.0929 50.084C20.699 50.1628 20.3838 50.3203 19.9899 50.3991C19.9111 49.2961 19.7535 48.0354 19.4383 46.8535C19.1232 45.8292 18.7292 44.8838 18.0989 43.9383C18.414 43.7019 18.7292 43.5443 19.0444 43.3867C19.6747 44.411 20.1474 45.4353 20.4626 46.5384C20.7778 47.7202 21.0141 48.9809 21.0929 50.084Z" fill="#575757"/>
<path d="M31.8082 48.5083C31.493 49.2962 31.6506 48.2719 31.5718 49.2174C31.5718 50.0841 31.8081 50.9508 32.2809 51.7387C33.2264 53.0782 34.881 53.8661 36.4568 54.4176C37.8751 54.8904 39.3721 55.1267 40.7903 54.4176C42.5237 53.5509 43.8632 51.5023 43.7056 49.5326C43.548 46.9325 40.4752 45.593 38.1902 45.3566C35.3537 45.0415 33.9355 46.0658 31.7294 47.7204V48.5083H31.8082Z" fill="#525252"/> <path d="M31.8079 48.5083C31.4927 49.2962 31.6503 48.2719 31.5715 49.2174C31.5715 50.0841 31.8079 50.9508 32.2807 51.7387C33.2261 53.0782 34.8808 53.8661 36.4566 54.4176C37.8748 54.8904 39.3719 55.1267 40.7901 54.4176C42.5235 53.5509 43.8629 51.5023 43.7054 49.5326C43.5478 46.9325 40.4749 45.593 38.19 45.3566C35.3535 45.0415 33.9353 46.0658 31.7291 47.7204V48.5083H31.8079Z" fill="#575757"/>
<path d="M53.7908 23.5314C53.7908 23.6102 53.712 23.7677 53.712 23.8465C53.3181 25.5799 52.8453 27.2346 52.6877 29.0467C52.6089 29.8347 52.7665 31.2529 51.9786 31.8044C51.3483 32.1984 50.2452 31.962 49.5361 31.962C47.3299 31.8044 45.2026 31.4105 42.9176 31.4105C42.7601 31.4105 42.6025 31.4105 42.3661 31.4105C41.5782 31.3317 40.8691 31.0165 40.2387 30.6226C39.372 30.2286 38.5841 29.7559 37.7174 29.4407C37.0083 29.2043 36.2204 29.0467 35.8264 28.4164C35.3537 27.7861 35.3537 26.9982 35.2749 26.2891C35.0385 23.9253 35.1173 21.404 35.7477 19.1191C36.1416 17.7008 36.9295 16.2826 38.1114 15.2583C40.7115 12.8946 44.8874 12.4218 48.1179 13.9976C50.4816 15.1795 52.5302 17.2281 53.4757 19.7494C53.8696 20.8525 54.0272 22.1919 53.7908 23.5314Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M53.7908 23.5314C53.7908 23.6102 53.712 23.7677 53.712 23.8465C53.3181 25.5799 52.8453 27.2346 52.6877 29.0467C52.6089 29.8347 52.7665 31.2529 51.9786 31.8044C51.3483 32.1984 50.2452 31.962 49.5361 31.962C47.3299 31.8044 45.2026 31.4105 42.9176 31.4105C42.7601 31.4105 42.6025 31.4105 42.3661 31.4105C41.5782 31.3317 40.8691 31.0165 40.2387 30.6226C39.372 30.2286 38.5841 29.7559 37.7174 29.4407C37.0083 29.2043 36.2204 29.0467 35.8264 28.4164C35.3537 27.7861 35.3537 26.9982 35.2749 26.2891C35.0385 23.9253 35.1173 21.404 35.7477 19.1191C36.1416 17.7008 36.9295 16.2826 38.1114 15.2583C40.7115 12.8946 44.8874 12.4218 48.1179 13.9976C50.4816 15.1795 52.5302 17.2281 53.4757 19.7494C53.8696 20.8525 54.0272 22.1919 53.7908 23.5314Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M53.7909 23.5315C53.7909 23.6103 53.7121 23.7679 53.7121 23.8466C53.3182 25.5801 52.8454 27.2347 52.6878 29.0469C52.6091 29.8348 52.7666 31.253 51.9787 31.8046C51.3484 32.1985 50.2453 31.9621 49.5362 31.9621C47.33 31.8046 45.2027 31.4106 42.9177 31.4106C42.7602 31.4106 42.6026 31.4106 42.3662 31.4106C43.3117 31.0166 44.336 31.0166 45.3603 30.8591C48.5907 30.3075 51.4272 28.0226 52.6878 25.0285C53.0818 24.083 53.3182 23.0587 53.397 22.0345C53.4758 21.2465 53.4758 20.4586 53.3182 19.7495C53.8697 20.8526 54.0273 22.192 53.7909 23.5315Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M53.7907 23.5315C53.7907 23.6103 53.7119 23.7679 53.7119 23.8466C53.3179 25.5801 52.8452 27.2347 52.6876 29.0469C52.6088 29.8348 52.7664 31.253 51.9785 31.8046C51.3482 32.1985 50.2451 31.9621 49.536 31.9621C47.3298 31.8046 45.2024 31.4106 42.9175 31.4106C42.7599 31.4106 42.6023 31.4106 42.366 31.4106C43.3115 31.0166 44.3357 31.0166 45.36 30.8591C48.5905 30.3075 51.4269 28.0226 52.6876 25.0285C53.0816 24.083 53.3179 23.0587 53.3967 22.0345C53.4755 21.2465 53.4755 20.4586 53.3179 19.7495C53.8695 20.8526 54.0271 22.192 53.7907 23.5315Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M46.6996 30.0716C45.8329 30.8595 44.8086 31.4111 43.7055 31.4111C41.8146 31.4111 39.8448 31.0171 38.1114 30.1504C37.5598 29.8353 37.0871 29.5201 36.6143 29.2049C36.378 28.9686 36.0628 28.7322 35.9052 28.4958C35.3537 27.8655 35.0385 27.1564 34.8809 26.2897C34.4082 24.4775 34.7234 22.2713 35.3537 20.5379C36.0628 18.7257 37.1659 18.0166 39.0569 17.9378C40.9479 17.7802 43.154 18.4105 44.9662 19.2772C45.045 19.356 45.1238 19.356 45.2026 19.4348C45.9905 19.8288 46.6996 20.3015 47.2511 20.8531C49.93 23.2956 49.2209 27.7079 46.6996 30.0716Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M46.6998 30.0716C45.8331 30.8595 44.8089 31.4111 43.7058 31.4111C41.8148 31.4111 39.845 31.0171 38.1116 30.1504C37.5601 29.8353 37.0873 29.5201 36.6146 29.2049C36.3782 28.9686 36.063 28.7322 35.9055 28.4958C35.3539 27.8655 35.0388 27.1564 34.8812 26.2897C34.4084 24.4775 34.7236 22.2713 35.3539 20.5379C36.063 18.7257 37.1661 18.0166 39.0571 17.9378C40.9481 17.7802 43.1542 18.4105 44.9664 19.2772C45.0452 19.356 45.124 19.356 45.2028 19.4348C45.9907 19.8288 46.6998 20.3015 47.2514 20.8531C49.9303 23.2956 49.2212 27.7079 46.6998 30.0716Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M42.5238 50.8722C42.5238 50.951 42.5238 50.951 42.5238 51.0298C42.5238 51.4238 42.445 51.7389 42.2874 52.0541C41.7359 53.5511 40.1601 54.4966 38.5843 54.9694C37.0084 55.4421 35.3538 55.5209 33.8568 55.9936C32.5961 56.3876 31.3355 56.9391 30.39 57.8058C30.1536 58.0422 29.9172 58.1998 29.7597 58.4362C29.7597 58.515 29.6809 58.515 29.6809 58.5938C27.9475 60.7211 27.4747 63.794 25.4261 65.685C25.2686 65.8425 25.111 65.9213 24.9534 66.0001C24.7958 66.0001 24.6382 65.9213 24.4807 65.9213C21.5654 64.6607 18.8865 63.0848 16.2076 61.4302C15.8136 61.1939 15.3409 60.8787 15.1045 60.4847C14.9469 60.1696 14.9469 59.8544 15.1045 59.5392C15.4985 58.3574 17.2319 56.9391 18.0198 56.624C18.8865 56.3088 19.9108 56.3876 20.7775 56.7028C21.6442 57.0179 22.5109 57.5695 23.22 58.121C23.3776 57.0179 23.6927 55.8361 24.1655 54.8118C24.3231 54.4966 24.4807 54.1815 24.6382 53.8663C24.7958 53.5511 25.0322 53.3147 25.1898 52.9996C25.6625 52.2905 26.2929 51.6601 26.9232 51.1086C27.7899 50.3207 28.7354 49.6116 29.6809 48.9812C31.0991 48.0357 32.5961 47.169 34.172 46.7751C34.4083 46.6963 34.6447 46.6963 34.8811 46.6175C36.4569 46.3023 38.1903 46.5387 39.6873 47.169C40.554 47.4842 41.342 48.0357 41.8935 48.7449C42.2874 49.2964 42.5238 50.0843 42.5238 50.8722Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M42.5238 50.8722C42.5238 50.951 42.5238 50.951 42.5238 51.0298C42.5238 51.4238 42.445 51.7389 42.2874 52.0541C41.7359 53.5511 40.1601 54.4966 38.5843 54.9694C37.0084 55.4421 35.3538 55.5209 33.8568 55.9936C32.5961 56.3876 31.3355 56.9391 30.39 57.8058C30.1536 58.0422 29.9172 58.1998 29.7597 58.4362C29.7597 58.515 29.6809 58.515 29.6809 58.5938C27.9475 60.7211 27.4747 63.794 25.4261 65.685C25.2686 65.8425 25.111 65.9213 24.9534 66.0001C24.7958 66.0001 24.6382 65.9213 24.4807 65.9213C21.5654 64.6607 18.8865 63.0848 16.2076 61.4302C15.8136 61.1939 15.3409 60.8787 15.1045 60.4847C14.9469 60.1696 14.9469 59.8544 15.1045 59.5392C15.4985 58.3574 17.2319 56.9391 18.0198 56.624C18.8865 56.3088 19.9108 56.3876 20.7775 56.7028C21.6442 57.0179 22.5109 57.5695 23.22 58.121C23.3776 57.0179 23.6927 55.8361 24.1655 54.8118C24.3231 54.4966 24.4807 54.1815 24.6382 53.8663C24.7958 53.5511 25.0322 53.3147 25.1898 52.9996C25.6625 52.2905 26.2929 51.6601 26.9232 51.1086C27.7899 50.3207 28.7354 49.6116 29.6809 48.9812C31.0991 48.0357 32.5961 47.169 34.172 46.7751C34.4083 46.6963 34.6447 46.6963 34.8811 46.6175C36.4569 46.3023 38.1903 46.5387 39.6873 47.169C40.554 47.4842 41.342 48.0357 41.8935 48.7449C42.2874 49.2964 42.5238 50.0843 42.5238 50.8722Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M30.3112 57.8055C30.0749 58.0419 29.8385 58.1995 29.6809 58.4359C29.6809 58.5146 29.6021 58.5146 29.6021 58.5934C28.6566 57.4116 27.5535 56.4661 26.2929 55.757C25.7414 55.4418 25.111 55.2054 24.4807 54.969C24.4019 54.8902 24.2443 54.8902 24.1655 54.8115C24.3231 54.4963 24.4807 54.1811 24.6383 53.866C24.7171 53.866 24.7959 53.9448 24.8746 53.9448C25.505 54.1811 26.2141 54.4175 26.8444 54.8115C28.1051 55.5206 29.3657 56.5449 30.3112 57.8055Z" fill="#525252"/> <path d="M30.3115 57.8055C30.0751 58.0419 29.8387 58.1995 29.6811 58.4359C29.6811 58.5146 29.6024 58.5146 29.6024 58.5934C28.6569 57.4116 27.5538 56.4661 26.2931 55.757C25.7416 55.4418 25.1113 55.2054 24.4809 54.969C24.4021 54.8902 24.2446 54.8902 24.1658 54.8115C24.3234 54.4963 24.4809 54.1811 24.6385 53.866C24.7173 53.866 24.7961 53.9448 24.8749 53.9448C25.5052 54.1811 26.2143 54.4175 26.8447 54.8115C28.1053 55.5206 29.366 56.5449 30.3115 57.8055Z" fill="#575757"/>
<path d="M66.2398 37.2411C66.2398 37.3199 66.2398 37.4775 66.161 37.5563C65.9247 38.423 65.058 38.8958 64.1913 39.0533C63.9549 39.1321 63.6397 39.1321 63.4033 39.1321C62.773 39.2109 62.2215 39.2109 61.5911 39.3685C60.9608 39.5261 60.3305 39.92 59.7002 40.2352C59.0698 40.5504 58.3607 40.7867 57.7304 40.9443C57.3364 41.0231 56.9425 41.1019 56.5485 41.1807C54.8151 41.4171 53.0817 41.2595 51.4271 40.708C50.4028 40.3928 49.6149 39.5261 49.1422 38.423C48.433 36.6896 48.5906 34.4835 50.2452 33.4592C52.3726 32.1197 53.712 34.0107 55.4454 34.7986C55.7606 34.9562 56.0758 35.035 56.3909 35.1138C56.7849 35.1926 57.1788 35.1926 57.5728 35.1926C58.4395 35.1138 59.3062 34.8774 60.0941 34.4835C61.5124 33.7743 62.773 32.6713 64.0337 31.647C64.27 31.4894 64.5064 31.253 64.8216 31.3318C65.1367 31.4106 65.2943 31.8046 65.2943 32.1985V32.2773C65.2155 32.5925 65.058 32.9076 64.9792 33.2228C64.8216 34.641 66.3974 35.9017 66.2398 37.2411Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M66.2398 37.2411C66.2398 37.3199 66.2398 37.4775 66.161 37.5563C65.9247 38.423 65.058 38.8958 64.1913 39.0533C63.9549 39.1321 63.6397 39.1321 63.4033 39.1321C62.773 39.2109 62.2215 39.2109 61.5911 39.3685C60.9608 39.5261 60.3305 39.92 59.7002 40.2352C59.0698 40.5504 58.3607 40.7867 57.7304 40.9443C57.3364 41.0231 56.9425 41.1019 56.5485 41.1807C54.8151 41.4171 53.0817 41.2595 51.4271 40.708C50.4028 40.3928 49.6149 39.5261 49.1422 38.423C48.433 36.6896 48.5906 34.4835 50.2452 33.4592C52.3726 32.1197 53.712 34.0107 55.4454 34.7986C55.7606 34.9562 56.0758 35.035 56.3909 35.1138C56.7849 35.1926 57.1788 35.1926 57.5728 35.1926C58.4395 35.1138 59.3062 34.8774 60.0941 34.4835C61.5124 33.7743 62.773 32.6713 64.0337 31.647C64.27 31.4894 64.5064 31.253 64.8216 31.3318C65.1367 31.4106 65.2943 31.8046 65.2943 32.1985V32.2773C65.2155 32.5925 65.058 32.9076 64.9792 33.2228C64.8216 34.641 66.3974 35.9017 66.2398 37.2411Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M65.6102 40.6295L59.7796 11.9495C59.7008 11.6343 59.9372 11.398 60.1736 11.3192C60.4887 11.2404 60.7251 11.4767 60.8039 11.7131L66.6344 40.3931C66.7132 40.7082 66.4769 40.9446 66.2405 41.0234C66.0041 41.1022 65.6889 40.8658 65.6102 40.6295Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M65.6099 40.6295L59.7794 11.9495C59.7006 11.6343 59.937 11.398 60.1733 11.3192C60.4885 11.2404 60.7249 11.4767 60.8037 11.7131L66.6342 40.3931C66.713 40.7082 66.4766 40.9446 66.2402 41.0234C66.0039 41.1022 65.6887 40.8658 65.6099 40.6295Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M66.1616 37.5562C65.9253 38.4229 65.0586 38.8956 64.1919 39.0532C63.9555 39.132 63.6403 39.132 63.404 39.132C63.404 39.0532 63.3252 38.9744 63.3252 38.8956C63.1676 38.5805 63.2464 38.1865 63.5615 37.9501C63.3252 37.8713 63.0888 37.7925 63.01 37.5562C62.8524 37.1622 63.01 36.6895 63.404 36.5319L63.6403 36.4531C63.5615 36.4531 63.4828 36.4531 63.4828 36.4531C63.1676 36.4531 62.9312 36.2955 62.7736 35.9803C62.6161 35.5864 62.7736 35.1924 63.0888 34.9561C62.9312 34.8773 62.6948 34.7197 62.6161 34.5621C62.4585 34.1682 62.6161 33.6954 63.01 33.5378L65.5313 32.356C65.4525 32.6711 65.2949 32.9863 65.2162 33.3014C64.8222 34.7197 66.6344 36.0591 66.1616 37.5562Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M66.1616 37.5562C65.9253 38.4229 65.0586 38.8956 64.1919 39.0532C63.9555 39.132 63.6403 39.132 63.404 39.132C63.404 39.0532 63.3252 38.9744 63.3252 38.8956C63.1676 38.5805 63.2464 38.1865 63.5615 37.9501C63.3252 37.8713 63.0888 37.7925 63.01 37.5562C62.8524 37.1622 63.01 36.6895 63.404 36.5319L63.6403 36.4531C63.5615 36.4531 63.4828 36.4531 63.4828 36.4531C63.1676 36.4531 62.9312 36.2955 62.7736 35.9803C62.6161 35.5864 62.7736 35.1924 63.0888 34.9561C62.9312 34.8773 62.6948 34.7197 62.6161 34.5621C62.4585 34.1682 62.6161 33.6954 63.01 33.5378L65.5313 32.356C65.4525 32.6711 65.2949 32.9863 65.2162 33.3014C64.8222 34.7197 66.6344 36.0591 66.1616 37.5562Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M67.4216 36.7685C67.3428 36.5321 67.1064 36.3745 66.8701 36.3745L67.1064 36.2958C67.5004 36.1382 67.658 35.6654 67.5004 35.2715C67.3428 34.9563 66.9488 34.7987 66.6337 34.8775L66.7913 34.7987C67.1852 34.6411 67.3428 34.1684 67.1852 33.7744C67.1064 33.5381 66.9489 33.4593 66.7125 33.3805C67.0276 33.1441 67.1852 32.7502 67.0276 32.3562C66.8701 31.9622 66.3973 31.8047 66.0034 31.9622L63.3245 33.2229C62.9305 33.3805 62.7729 33.8532 62.9305 34.2472C63.0093 34.4836 63.1669 34.5623 63.4032 34.6411C63.0881 34.8775 62.9305 35.2715 63.0881 35.6654C63.2457 35.9806 63.6396 36.1382 63.9548 36.0594L63.7184 36.1382C63.3245 36.2958 63.1669 36.7685 63.3245 37.1625C63.4032 37.3988 63.6396 37.5564 63.876 37.5564C63.5608 37.7928 63.482 38.1867 63.6396 38.5019C63.7972 38.8959 64.2699 39.0534 64.6639 38.8959L67.0276 37.7928C67.4216 37.6352 67.5792 37.2412 67.4216 36.7685Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M67.4217 36.7685C67.3429 36.5321 67.1066 36.3745 66.8702 36.3745L67.1066 36.2958C67.5005 36.1382 67.6581 35.6654 67.5005 35.2715C67.3429 34.9563 66.949 34.7987 66.6338 34.8775L66.7914 34.7987C67.1853 34.6411 67.3429 34.1684 67.1853 33.7744C67.1066 33.5381 66.949 33.4593 66.7126 33.3805C67.0278 33.1441 67.1853 32.7502 67.0278 32.3562C66.8702 31.9622 66.3974 31.8047 66.0035 31.9622L63.3246 33.2229C62.9306 33.3805 62.773 33.8532 62.9306 34.2472C63.0094 34.4836 63.167 34.5623 63.4034 34.6411C63.0882 34.8775 62.9306 35.2715 63.0882 35.6654C63.2458 35.9806 63.6397 36.1382 63.9549 36.0594L63.7185 36.1382C63.3246 36.2958 63.167 36.7685 63.3246 37.1625C63.4034 37.3988 63.6397 37.5564 63.8761 37.5564C63.561 37.7928 63.4822 38.1867 63.6397 38.5019C63.7973 38.8959 64.2701 39.0534 64.664 38.8959L67.0278 37.7928C67.4217 37.6352 67.5793 37.2412 67.4217 36.7685Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path opacity="0.4" d="M43.7846 22.4287C43.4695 22.9014 42.8391 23.059 42.3664 22.8226C42.2088 22.7438 41.9724 22.665 41.8149 22.5862C39.6087 21.7195 37.245 22.2711 36.6146 23.9257C36.4571 24.3196 35.9055 24.2408 35.9055 23.8469C35.8267 23.2954 35.9055 22.665 36.1419 22.1135C36.9298 19.9861 39.5299 19.0406 41.9724 19.9861C42.6028 20.2225 43.1543 20.6165 43.6271 21.0104C44.021 21.4044 44.021 21.9559 43.7846 22.4287Z" fill="#525252"/> <path opacity="0.4" d="M43.7846 22.4287C43.4695 22.9014 42.8391 23.059 42.3664 22.8226C42.2088 22.7438 41.9724 22.665 41.8149 22.5862C39.6087 21.7195 37.245 22.2711 36.6146 23.9257C36.4571 24.3196 35.9055 24.2408 35.9055 23.8469C35.8267 23.2954 35.9055 22.665 36.1419 22.1135C36.9298 19.9861 39.5299 19.0406 41.9724 19.9861C42.6028 20.2225 43.1543 20.6165 43.6271 21.0104C44.021 21.4044 44.021 21.9559 43.7846 22.4287Z" fill="#575757"/>
<path opacity="0.4" d="M36.2208 34.4835C37.7966 35.2714 41.0271 36.2956 42.9968 36.4532C43.3908 36.532 43.5484 36.8472 43.4696 37.2411C42.1301 40.3928 41.0271 43.5444 40.6331 44.7263C40.5543 45.0415 40.2392 45.199 40.0028 45.1203C38.5845 44.7263 34.645 43.6232 33.0692 42.9929C32.754 42.9141 32.6752 42.5989 32.754 42.2838C34.0935 38.9745 35.0389 36.0593 35.4329 34.7986C35.5905 34.4835 35.9844 34.3259 36.2208 34.4835Z" fill="#525252"/> <path opacity="0.4" d="M36.2206 34.4835C37.7964 35.2714 41.0268 36.2956 42.9966 36.4532C43.3906 36.532 43.5481 36.8472 43.4693 37.2411C42.1299 40.3928 41.0268 43.5444 40.6329 44.7263C40.5541 45.0415 40.2389 45.199 40.0025 45.1203C38.5843 44.7263 34.6447 43.6232 33.0689 42.9929C32.7538 42.9141 32.675 42.5989 32.7538 42.2838C34.0932 38.9745 35.0387 36.0593 35.4327 34.7986C35.5902 34.4835 35.9842 34.3259 36.2206 34.4835Z" fill="#575757"/>
<path d="M57.8097 40.8655C57.4158 40.9443 57.0218 41.0231 56.6278 41.1019C55.9975 40.1564 55.6036 38.8957 55.6824 37.6351C55.7611 36.926 55.9975 35.9017 56.4703 35.1138C56.8642 35.1926 57.2582 35.1926 57.6521 35.1926C57.1794 35.9017 56.7854 36.926 56.7066 37.7139C56.6278 38.8957 57.1006 40.1564 57.8097 40.8655Z" fill="#525252"/> <path d="M57.8097 40.8655C57.4158 40.9443 57.0218 41.0231 56.6278 41.1019C55.9975 40.1564 55.6036 38.8957 55.6824 37.6351C55.7611 36.926 55.9975 35.9017 56.4703 35.1138C56.8642 35.1926 57.2582 35.1926 57.6521 35.1926C57.1794 35.9017 56.7854 36.926 56.7066 37.7139C56.6278 38.8957 57.1006 40.1564 57.8097 40.8655Z" fill="#575757"/>
<path d="M29.8384 30.6228C28.4202 31.5683 27.1595 32.7501 26.0565 34.0896C25.8201 33.8532 25.5049 33.538 25.2686 33.3017C26.2928 32.041 27.5535 30.9379 28.8929 29.9924L29.8384 30.6228Z" fill="#525252"/> <path d="M29.8387 30.6228C28.4204 31.5683 27.1598 32.7501 26.0567 34.0896C25.8203 33.8532 25.5052 33.538 25.2688 33.3017C26.2931 32.041 27.5537 30.9379 28.8932 29.9924L29.8387 30.6228Z" fill="#575757"/>
<path d="M81.0531 22.2708C73.6468 19.8283 70.5739 29.1257 63.01 25.1861L60.3311 12.1068C68.5253 15.5736 71.677 7.69449 78.3742 9.19152L81.0531 22.2708Z" fill="#181818" stroke="#525252" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/> <path d="M81.053 22.2708C73.6466 19.8283 70.5738 29.1257 63.0098 25.1861L60.3309 12.1068C68.5252 15.5736 71.6769 7.69449 78.3741 9.19152L81.053 22.2708Z" fill="#1E1E1E" stroke="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Some files were not shown because too many files have changed in this diff Show More