Compare commits
86 Commits
2023.12.1
...
feat/reque
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3e941271e | ||
|
|
e7fded89d5 | ||
|
|
40c8d7e4b7 | ||
|
|
1d93d9dabe | ||
|
|
e9f1dc7ba1 | ||
|
|
1e906bbec3 | ||
|
|
7776668e40 | ||
|
|
3d38b7678b | ||
|
|
1ca215d887 | ||
|
|
aec04a13e3 | ||
|
|
9a2058bbec | ||
|
|
1c65147a77 | ||
|
|
207eea538f | ||
|
|
e30b59e1b5 | ||
|
|
9704b3324b | ||
|
|
386bb453d4 | ||
|
|
175641246e | ||
|
|
fb615d2d2b | ||
|
|
7685409ee6 | ||
|
|
e01818444e | ||
|
|
52220d9a2e | ||
|
|
82d687f665 | ||
|
|
d44083a380 | ||
|
|
7589c57e86 | ||
|
|
c8009aec77 | ||
|
|
7114f4fd43 | ||
|
|
ca1a4ec31b | ||
|
|
359633102e | ||
|
|
b278691a9d | ||
|
|
c27e1be230 | ||
|
|
11e8ba7bb3 | ||
|
|
2a0000cfc4 | ||
|
|
0ed063724e | ||
|
|
170ec15821 | ||
|
|
3611cac241 | ||
|
|
919579b1da | ||
|
|
4798d7bbbd | ||
|
|
a0c6b22641 | ||
|
|
de8929ab18 | ||
|
|
55a94bdccc | ||
|
|
faab1d20fd | ||
|
|
bd406616ec | ||
|
|
6827e97ec5 | ||
|
|
10d2048975 | ||
|
|
291f18591e | ||
|
|
342532c9b1 | ||
|
|
cf039c482a | ||
|
|
ded2725116 | ||
|
|
9c6754c70f | ||
|
|
4bd54b12cd | ||
|
|
ed6e9b6954 | ||
|
|
dfdd44b4ed | ||
|
|
fc34871dae | ||
|
|
45b532747e | ||
|
|
de4635df23 | ||
|
|
41bad1f3dc | ||
|
|
ecca3d2032 | ||
|
|
47226be6d0 | ||
|
|
6a0e73fdec | ||
|
|
672ee69b2c | ||
|
|
b359650d96 | ||
|
|
c0fae79678 | ||
|
|
5bcc38e36b | ||
|
|
00862eb192 | ||
|
|
16803acb26 | ||
|
|
3911c9cd1f | ||
|
|
0028f6e878 | ||
|
|
0ba33ec187 | ||
|
|
3482743782 | ||
|
|
d7cdeb796a | ||
|
|
3d6adcc39d | ||
|
|
aab76f1358 | ||
|
|
a28a576c41 | ||
|
|
0d0ad7a2f8 | ||
|
|
1df9de44b7 | ||
|
|
4cba03e53f | ||
|
|
9e1466a877 | ||
|
|
b81ccb4ee3 | ||
|
|
27d0a7c437 | ||
|
|
aca96dd5f2 | ||
|
|
c0dbcc901f | ||
|
|
ba52c8cc37 | ||
|
|
d1f6f40ef8 | ||
|
|
99f5070f71 | ||
|
|
cd371fc9d4 | ||
|
|
59fef248c0 |
@@ -239,7 +239,7 @@ Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) f
|
||||
|
||||
📦 **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.
|
||||
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience.
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ services:
|
||||
build:
|
||||
dockerfile: packages/hoppscotch-backend/Dockerfile
|
||||
context: .
|
||||
target: prod
|
||||
target: dev
|
||||
env_file:
|
||||
- ./.env
|
||||
restart: always
|
||||
@@ -122,7 +122,7 @@ services:
|
||||
- PORT=3000
|
||||
volumes:
|
||||
# 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/
|
||||
depends_on:
|
||||
hoppscotch-db:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hoppscotch-backend",
|
||||
"version": "2023.12.1",
|
||||
"version": "2023.12.6",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@@ -34,12 +34,14 @@
|
||||
"@nestjs/jwt": "^10.1.1",
|
||||
"@nestjs/passport": "^10.0.2",
|
||||
"@nestjs/platform-express": "^10.2.6",
|
||||
"@nestjs/schedule": "^4.0.1",
|
||||
"@nestjs/throttler": "^5.0.0",
|
||||
"@prisma/client": "^4.16.2",
|
||||
"@prisma/client": "^5.8.0",
|
||||
"argon2": "^0.30.3",
|
||||
"bcrypt": "^5.1.0",
|
||||
"cookie": "^0.5.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cron": "^3.1.6",
|
||||
"express": "^4.17.1",
|
||||
"express-session": "^1.17.3",
|
||||
"fp-ts": "^2.13.1",
|
||||
@@ -57,7 +59,8 @@
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^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",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.6.0"
|
||||
|
||||
@@ -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);
|
||||
@@ -41,31 +41,31 @@ model TeamInvitation {
|
||||
}
|
||||
|
||||
model TeamCollection {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
parentID String?
|
||||
data Json?
|
||||
parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id])
|
||||
children TeamCollection[] @relation("TeamCollectionChildParent")
|
||||
parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id])
|
||||
children TeamCollection[] @relation("TeamCollectionChildParent")
|
||||
requests TeamRequest[]
|
||||
teamID String
|
||||
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
|
||||
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
orderIndex Int
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
}
|
||||
|
||||
model TeamRequest {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
collectionID String
|
||||
collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade)
|
||||
collection TeamCollection @relation(fields: [collectionID], references: [id], onDelete: Cascade)
|
||||
teamID String
|
||||
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
|
||||
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
|
||||
title String
|
||||
request Json
|
||||
orderIndex Int
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
createdOn DateTime @default(now()) @db.Timestamp(3)
|
||||
updatedOn DateTime @updatedAt @db.Timestamp(3)
|
||||
}
|
||||
|
||||
model Shortcode {
|
||||
|
||||
@@ -27,9 +27,7 @@ import {
|
||||
} from './input-types.args';
|
||||
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
|
||||
import { SkipThrottle } from '@nestjs/throttler';
|
||||
import { User } from 'src/user/user.model';
|
||||
import { PaginationArgs } from 'src/types/input-types.args';
|
||||
import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
|
||||
import { UserDeletionResult } from 'src/user/user.model';
|
||||
|
||||
@UseGuards(GqlThrottlerGuard)
|
||||
@Resolver(() => Admin)
|
||||
@@ -49,203 +47,6 @@ export class AdminResolver {
|
||||
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 */
|
||||
|
||||
@Mutation(() => InvitedUser, {
|
||||
@@ -269,8 +70,26 @@ export class AdminResolver {
|
||||
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, {
|
||||
description: 'Delete an user account from infra',
|
||||
deprecationReason: 'Use removeUsersByAdmin instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async removeUserByAdmin(
|
||||
@@ -281,12 +100,33 @@ export class AdminResolver {
|
||||
})
|
||||
userUID: string,
|
||||
): Promise<boolean> {
|
||||
const invitedUser = await this.adminService.removeUserAccount(userUID);
|
||||
if (E.isLeft(invitedUser)) throwErr(invitedUser.left);
|
||||
return invitedUser.right;
|
||||
const removedUser = await this.adminService.removeUserAccount(userUID);
|
||||
if (E.isLeft(removedUser)) throwErr(removedUser.left);
|
||||
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, {
|
||||
description: 'Make user an admin',
|
||||
deprecationReason: 'Use makeUsersAdmin instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async makeUserAdmin(
|
||||
@@ -302,8 +142,51 @@ export class AdminResolver {
|
||||
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, {
|
||||
description: 'Remove user as admin',
|
||||
deprecationReason: 'Use demoteUsersByAdmin instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async removeUserAsAdmin(
|
||||
@@ -319,6 +202,23 @@ export class AdminResolver {
|
||||
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, {
|
||||
description:
|
||||
'Create a new team by providing the user uid to nominate as Team owner',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AdminService } from './admin.service';
|
||||
import { PubSubService } from '../pubsub/pubsub.service';
|
||||
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 { TeamService } from '../team/team.service';
|
||||
import { TeamEnvironmentsService } from '../team-environments/team-environments.service';
|
||||
@@ -13,10 +13,15 @@ import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import {
|
||||
DUPLICATE_EMAIL,
|
||||
INVALID_EMAIL,
|
||||
ONLY_ONE_ADMIN_ACCOUNT,
|
||||
USER_ALREADY_INVITED,
|
||||
USER_INVITATION_DELETION_FAILED,
|
||||
USER_NOT_FOUND,
|
||||
} from '../errors';
|
||||
import { ShortcodeService } from 'src/shortcode/shortcode.service';
|
||||
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 mockPubSub = mockDeep<PubSubService>();
|
||||
@@ -58,20 +63,87 @@ const invitedUsers: InvitedUsers[] = [
|
||||
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('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
|
||||
// @ts-ignore
|
||||
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
|
||||
// @ts-ignore
|
||||
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);
|
||||
});
|
||||
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([]);
|
||||
|
||||
const results = await adminService.fetchInvitedUsers();
|
||||
const results = await adminService.fetchInvitedUsers(paginationArgs);
|
||||
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', () => {
|
||||
test('should return count of all users in the organization', async () => {
|
||||
mockUserService.getUsersCount.mockResolvedValueOnce(10);
|
||||
|
||||
@@ -6,13 +6,16 @@ import * as E from 'fp-ts/Either';
|
||||
import * as O from 'fp-ts/Option';
|
||||
import { validateEmail } from '../utils';
|
||||
import {
|
||||
ADMIN_CAN_NOT_BE_DELETED,
|
||||
DUPLICATE_EMAIL,
|
||||
EMAIL_FAILED,
|
||||
INVALID_EMAIL,
|
||||
ONLY_ONE_ADMIN_ACCOUNT,
|
||||
TEAM_INVITE_ALREADY_MEMBER,
|
||||
TEAM_INVITE_NO_INVITE_FOUND,
|
||||
USERS_NOT_FOUND,
|
||||
USER_ALREADY_INVITED,
|
||||
USER_INVITATION_DELETION_FAILED,
|
||||
USER_IS_ADMIN,
|
||||
USER_NOT_FOUND,
|
||||
} from '../errors';
|
||||
@@ -26,6 +29,8 @@ import { TeamInvitationService } from '../team-invitation/team-invitation.servic
|
||||
import { TeamMemberRole } from '../team/team.model';
|
||||
import { ShortcodeService } from 'src/shortcode/shortcode.service';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
||||
import { UserDeletionResult } from 'src/user/user.model';
|
||||
|
||||
@Injectable()
|
||||
export class AdminService {
|
||||
@@ -48,12 +53,30 @@ export class AdminService {
|
||||
* @param cursorID Users uid
|
||||
* @param take number of users to fetch
|
||||
* @returns an Either of array of user or error
|
||||
* @deprecated use fetchUsersV2 instead
|
||||
*/
|
||||
async fetchUsers(cursorID: string, take: number) {
|
||||
const allUsers = await this.userService.fetchAllUsers(cursorID, take);
|
||||
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.
|
||||
* @param adminUID Admin's UID
|
||||
@@ -110,14 +133,68 @@ export class AdminService {
|
||||
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.
|
||||
* @returns an Either of array of `InvitedUser` object or error
|
||||
*/
|
||||
async fetchInvitedUsers() {
|
||||
const invitedUsers = await this.prisma.invitedUsers.findMany();
|
||||
async fetchInvitedUsers(paginationOption: OffsetPaginationArgs) {
|
||||
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 },
|
||||
);
|
||||
|
||||
@@ -337,6 +414,7 @@ export class AdminService {
|
||||
* Remove a user account by UID
|
||||
* @param userUid User UID
|
||||
* @returns an Either of boolean or error
|
||||
* @deprecated use removeUserAccounts instead
|
||||
*/
|
||||
async removeUserAccount(userUid: string) {
|
||||
const user = await this.userService.findUserById(userUid);
|
||||
@@ -349,10 +427,73 @@ export class AdminService {
|
||||
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
|
||||
* @param userUid User UID
|
||||
* @returns an Either of boolean or error
|
||||
* @deprecated use makeUsersAdmin instead
|
||||
*/
|
||||
async makeUserAdmin(userUID: string) {
|
||||
const admin = await this.userService.makeAdmin(userUID);
|
||||
@@ -360,10 +501,22 @@ export class AdminService {
|
||||
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
|
||||
* @param userUid User UID
|
||||
* @returns an Either of boolean or error
|
||||
* @deprecated use demoteUsersByAdmin instead
|
||||
*/
|
||||
async removeUserAsAdmin(userUID: string) {
|
||||
const adminUsers = await this.userService.fetchAdminUsers();
|
||||
@@ -374,6 +527,26 @@ export class AdminService {
|
||||
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
|
||||
* @returns number of users in the org
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,10 @@ import { AuthUser } from 'src/types/AuthUser';
|
||||
import { throwErr } from 'src/utils';
|
||||
import * as E from 'fp-ts/Either';
|
||||
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 { Team } from 'src/team/team.model';
|
||||
import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
|
||||
@@ -29,7 +32,8 @@ import {
|
||||
EnableAndDisableSSOArgs,
|
||||
InfraConfigArgs,
|
||||
} 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)
|
||||
@Resolver(() => Infra)
|
||||
@@ -76,6 +80,7 @@ export class InfraResolver {
|
||||
|
||||
@ResolveField(() => [User], {
|
||||
description: 'Returns a list of all the users in infra',
|
||||
deprecationReason: 'Use allUsersV2 instead',
|
||||
})
|
||||
@UseGuards(GqlAuthGuard, GqlAdminGuard)
|
||||
async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> {
|
||||
@@ -83,11 +88,33 @@ export class InfraResolver {
|
||||
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], {
|
||||
description: 'Returns a list of all the invited users',
|
||||
})
|
||||
async invitedUsers(): Promise<InvitedUser[]> {
|
||||
const users = await this.adminService.fetchInvitedUsers();
|
||||
async invitedUsers(
|
||||
@Args() args: OffsetPaginationArgs,
|
||||
): Promise<InvitedUser[]> {
|
||||
const users = await this.adminService.fetchInvitedUsers(args);
|
||||
return users;
|
||||
}
|
||||
|
||||
@@ -247,10 +274,10 @@ export class InfraResolver {
|
||||
async infraConfigs(
|
||||
@Args({
|
||||
name: 'configNames',
|
||||
type: () => [InfraConfigEnumForClient],
|
||||
type: () => [InfraConfigEnum],
|
||||
description: 'Configs to fetch',
|
||||
})
|
||||
names: InfraConfigEnumForClient[],
|
||||
names: InfraConfigEnum[],
|
||||
) {
|
||||
const infraConfigs = await this.infraConfigService.getMany(names);
|
||||
if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left);
|
||||
@@ -284,6 +311,25 @@ export class InfraResolver {
|
||||
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, {
|
||||
description: 'Reset Infra Configs with default values (.env)',
|
||||
})
|
||||
@@ -306,7 +352,9 @@ export class InfraResolver {
|
||||
})
|
||||
providerInfo: EnableAndDisableSSOArgs[],
|
||||
) {
|
||||
const isUpdated = await this.infraConfigService.enableAndDisableSSO(providerInfo);
|
||||
const isUpdated = await this.infraConfigService.enableAndDisableSSO(
|
||||
providerInfo,
|
||||
);
|
||||
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -24,6 +24,8 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { InfraConfigModule } from './infra-config/infra-config.module';
|
||||
import { loadInfraConfiguration } from './infra-config/helper';
|
||||
import { MailerModule } from './mailer/mailer.module';
|
||||
import { PosthogModule } from './posthog/posthog.module';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -96,6 +98,8 @@ import { MailerModule } from './mailer/mailer.module';
|
||||
UserCollectionModule,
|
||||
ShortcodeModule,
|
||||
InfraConfigModule,
|
||||
PosthogModule,
|
||||
ScheduleModule.forRoot(),
|
||||
],
|
||||
providers: [GQLComplexityPlugin],
|
||||
controllers: [AppController],
|
||||
|
||||
@@ -18,12 +18,7 @@ import { JwtAuthGuard } from './guards/jwt-auth.guard';
|
||||
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
||||
import { AuthUser } from 'src/types/AuthUser';
|
||||
import { RTCookie } from 'src/decorators/rt-cookie.decorator';
|
||||
import {
|
||||
AuthProvider,
|
||||
authCookieHandler,
|
||||
authProviderCheck,
|
||||
throwHTTPErr,
|
||||
} from './helper';
|
||||
import { AuthProvider, authCookieHandler, authProviderCheck } from './helper';
|
||||
import { GoogleSSOGuard } from './guards/google-sso.guard';
|
||||
import { GithubSSOGuard } from './guards/github-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 { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { throwHTTPErr } from 'src/utils';
|
||||
|
||||
@UseGuards(ThrottlerBehindProxyGuard)
|
||||
@Controller({ path: 'auth', version: '1' })
|
||||
|
||||
@@ -12,7 +12,10 @@ import { GithubStrategy } from './strategies/github.strategy';
|
||||
import { MicrosoftStrategy } from './strategies/microsoft.strategy';
|
||||
import { AuthProvider, authProviderCheck } from './helper';
|
||||
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';
|
||||
|
||||
@Module({
|
||||
@@ -34,6 +37,11 @@ import { InfraConfigModule } from 'src/infra-config/infra-config.module';
|
||||
})
|
||||
export class AuthModule {
|
||||
static async register() {
|
||||
const isInfraConfigPopulated = await isInfraConfigTablePopulated();
|
||||
if (!isInfraConfigPopulated) {
|
||||
return { module: AuthModule };
|
||||
}
|
||||
|
||||
const env = await loadInfraConfiguration();
|
||||
const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
RefreshTokenPayload,
|
||||
} from 'src/types/AuthTokens';
|
||||
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 { VerificationToken } from '@prisma/client';
|
||||
import { Origin } from './helper';
|
||||
@@ -117,7 +117,7 @@ export class AuthService {
|
||||
userUid,
|
||||
);
|
||||
if (E.isLeft(updatedUser))
|
||||
return E.left(<AuthError>{
|
||||
return E.left(<RESTError>{
|
||||
message: updatedUser.left,
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
});
|
||||
@@ -255,7 +255,7 @@ export class AuthService {
|
||||
*/
|
||||
async verifyMagicLinkTokens(
|
||||
magicLinkIDTokens: VerifyMagicDto,
|
||||
): Promise<E.Right<AuthTokens> | E.Left<AuthError>> {
|
||||
): Promise<E.Right<AuthTokens> | E.Left<RESTError>> {
|
||||
const passwordlessTokens = await this.validatePasswordlessTokens(
|
||||
magicLinkIDTokens,
|
||||
);
|
||||
@@ -373,7 +373,7 @@ export class AuthService {
|
||||
if (usersCount === 1) {
|
||||
const elevatedUser = await this.usersService.makeAdmin(user.uid);
|
||||
if (E.isLeft(elevatedUser))
|
||||
return E.left(<AuthError>{
|
||||
return E.left(<RESTError>{
|
||||
message: elevatedUser.left,
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||
import { AuthProvider, authProviderCheck } from '../helper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { throwHTTPErr } from 'src/utils';
|
||||
|
||||
@Injectable()
|
||||
export class GithubSSOGuard extends AuthGuard('github') implements CanActivate {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||
import { AuthProvider, authProviderCheck } from '../helper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { throwHTTPErr } from 'src/utils';
|
||||
|
||||
@Injectable()
|
||||
export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||
import { AuthProvider, authProviderCheck } from '../helper';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { throwHTTPErr } from 'src/utils';
|
||||
|
||||
@Injectable()
|
||||
export class MicrosoftSSOGuard
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { DateTime } from 'luxon';
|
||||
import { AuthError } from 'src/types/AuthError';
|
||||
import { AuthTokens } from 'src/types/AuthTokens';
|
||||
import { Response } from 'express';
|
||||
import * as cookie from 'cookie';
|
||||
@@ -25,15 +24,6 @@ export enum AuthProvider {
|
||||
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
|
||||
* @param res Express Response Object
|
||||
|
||||
@@ -17,8 +17,8 @@ export class GithubStrategy extends PassportStrategy(Strategy) {
|
||||
super({
|
||||
clientID: configService.get('INFRA.GITHUB_CLIENT_ID'),
|
||||
clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'),
|
||||
callbackURL: configService.get('GITHUB_CALLBACK_URL'),
|
||||
scope: [configService.get('GITHUB_SCOPE')],
|
||||
callbackURL: configService.get('INFRA.GITHUB_CALLBACK_URL'),
|
||||
scope: [configService.get('INFRA.GITHUB_SCOPE')],
|
||||
store: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ export class GoogleStrategy extends PassportStrategy(Strategy) {
|
||||
super({
|
||||
clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'),
|
||||
clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'),
|
||||
callbackURL: configService.get('GOOGLE_CALLBACK_URL'),
|
||||
scope: configService.get('GOOGLE_SCOPE').split(','),
|
||||
callbackURL: configService.get('INFRA.GOOGLE_CALLBACK_URL'),
|
||||
scope: configService.get('INFRA.GOOGLE_SCOPE').split(','),
|
||||
passReqToCallback: true,
|
||||
store: true,
|
||||
});
|
||||
|
||||
@@ -17,9 +17,9 @@ export class MicrosoftStrategy extends PassportStrategy(Strategy) {
|
||||
super({
|
||||
clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'),
|
||||
clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'),
|
||||
callbackURL: configService.get('MICROSOFT_CALLBACK_URL'),
|
||||
scope: [configService.get('MICROSOFT_SCOPE')],
|
||||
tenant: configService.get('MICROSOFT_TENANT'),
|
||||
callbackURL: configService.get('INFRA.MICROSOFT_CALLBACK_URL'),
|
||||
scope: [configService.get('INFRA.MICROSOFT_SCOPE')],
|
||||
tenant: configService.get('INFRA.MICROSOFT_TENANT'),
|
||||
store: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ export const DUPLICATE_EMAIL = 'email/both_emails_cannot_be_same' as const;
|
||||
export const ONLY_ONE_ADMIN_ACCOUNT =
|
||||
'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)
|
||||
* (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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (TeamsService)
|
||||
@@ -213,6 +228,12 @@ export const TEAM_COL_NOT_SAME_PARENT =
|
||||
export const TEAM_COL_SAME_NEXT_COLL =
|
||||
'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
|
||||
* (TeamCollectionService)
|
||||
@@ -268,6 +289,13 @@ export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
|
||||
export const TEAM_COLL_DATA_INVALID =
|
||||
'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
|
||||
* (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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* (AuthService)
|
||||
@@ -690,9 +731,22 @@ export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const;
|
||||
export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED =
|
||||
'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
|
||||
* (InfraConfigService)
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
* PostHog client is not initialized
|
||||
* (InfraConfigService)
|
||||
*/
|
||||
export const POSTHOG_CLIENT_NOT_INITIALIZED = 'posthog/client_not_initialized';
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
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 { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import { throwErr } from 'src/utils';
|
||||
import { randomBytes } from 'crypto';
|
||||
|
||||
export enum ServiceStatus {
|
||||
ENABLE = 'ENABLE',
|
||||
@@ -13,14 +17,21 @@ const AuthProviderConfigurations = {
|
||||
[AuthProvider.GOOGLE]: [
|
||||
InfraConfigEnum.GOOGLE_CLIENT_ID,
|
||||
InfraConfigEnum.GOOGLE_CLIENT_SECRET,
|
||||
InfraConfigEnum.GOOGLE_CALLBACK_URL,
|
||||
InfraConfigEnum.GOOGLE_SCOPE,
|
||||
],
|
||||
[AuthProvider.GITHUB]: [
|
||||
InfraConfigEnum.GITHUB_CLIENT_ID,
|
||||
InfraConfigEnum.GITHUB_CLIENT_SECRET,
|
||||
InfraConfigEnum.GITHUB_CALLBACK_URL,
|
||||
InfraConfigEnum.GITHUB_SCOPE,
|
||||
],
|
||||
[AuthProvider.MICROSOFT]: [
|
||||
InfraConfigEnum.MICROSOFT_CLIENT_ID,
|
||||
InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
|
||||
InfraConfigEnum.MICROSOFT_CALLBACK_URL,
|
||||
InfraConfigEnum.MICROSOFT_SCOPE,
|
||||
InfraConfigEnum.MICROSOFT_TENANT,
|
||||
],
|
||||
[AuthProvider.EMAIL]: [
|
||||
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
|
||||
* (Docker will re-start the app)
|
||||
@@ -104,3 +234,12 @@ export function getConfiguredSSOProviders() {
|
||||
|
||||
return configuredAuthProviders.join(',');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a hashed valued for analytics
|
||||
* @returns Generated hashed value
|
||||
*/
|
||||
export function generateAnalyticsUserId() {
|
||||
const hashedUserID = randomBytes(20).toString('hex');
|
||||
return hashedUserID;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||
import { AuthProvider } from 'src/auth/helper';
|
||||
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
|
||||
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import { ServiceStatus } from './helper';
|
||||
|
||||
@ObjectType()
|
||||
@@ -8,7 +8,7 @@ export class InfraConfig {
|
||||
@Field({
|
||||
description: 'Infra Config Name',
|
||||
})
|
||||
name: InfraConfigEnumForClient;
|
||||
name: InfraConfigEnum;
|
||||
|
||||
@Field({
|
||||
description: 'Infra Config Value',
|
||||
@@ -16,7 +16,7 @@ export class InfraConfig {
|
||||
value: string;
|
||||
}
|
||||
|
||||
registerEnumType(InfraConfigEnumForClient, {
|
||||
registerEnumType(InfraConfigEnum, {
|
||||
name: 'InfraConfigEnum',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InfraConfigService } from './infra-config.service';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { SiteController } from './infra-config.controller';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [InfraConfigService],
|
||||
exports: [InfraConfigService],
|
||||
controllers: [SiteController],
|
||||
})
|
||||
export class InfraConfigModule {}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { mockDeep, mockReset } from 'jest-mock-extended';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { InfraConfigService } from './infra-config.service';
|
||||
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import {
|
||||
InfraConfigEnum,
|
||||
InfraConfigEnumForClient,
|
||||
} from 'src/types/InfraConfig';
|
||||
import { INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_UPDATE_FAILED } from 'src/errors';
|
||||
INFRA_CONFIG_NOT_FOUND,
|
||||
INFRA_CONFIG_OPERATION_NOT_ALLOWED,
|
||||
INFRA_CONFIG_UPDATE_FAILED,
|
||||
} from 'src/errors';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as helper from './helper';
|
||||
import { InfraConfig as dbInfraConfig } from '@prisma/client';
|
||||
import { InfraConfig } from './infra-config.model';
|
||||
|
||||
const mockPrisma = mockDeep<PrismaService>();
|
||||
const mockConfigService = mockDeep<ConfigService>();
|
||||
@@ -19,12 +22,82 @@ const infraConfigService = new InfraConfigService(
|
||||
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(() => {
|
||||
mockReset(mockPrisma);
|
||||
});
|
||||
|
||||
describe('InfraConfigService', () => {
|
||||
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 () => {
|
||||
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
|
||||
const value = 'true';
|
||||
@@ -71,7 +144,7 @@ describe('InfraConfigService', () => {
|
||||
|
||||
describe('get', () => {
|
||||
it('should get the infra config', async () => {
|
||||
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
|
||||
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
|
||||
const value = 'true';
|
||||
|
||||
mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({
|
||||
@@ -87,7 +160,7 @@ describe('InfraConfigService', () => {
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -98,7 +171,7 @@ describe('InfraConfigService', () => {
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
@@ -106,4 +179,45 @@ describe('InfraConfigService', () => {
|
||||
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)),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,23 +3,25 @@ import { InfraConfig } from './infra-config.model';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { InfraConfig as DBInfraConfig } from '@prisma/client';
|
||||
import * as E from 'fp-ts/Either';
|
||||
import {
|
||||
InfraConfigEnum,
|
||||
InfraConfigEnumForClient,
|
||||
} from 'src/types/InfraConfig';
|
||||
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import {
|
||||
AUTH_PROVIDER_NOT_SPECIFIED,
|
||||
DATABASE_TABLE_NOT_EXIST,
|
||||
INFRA_CONFIG_INVALID_INPUT,
|
||||
INFRA_CONFIG_NOT_FOUND,
|
||||
INFRA_CONFIG_NOT_LISTED,
|
||||
INFRA_CONFIG_RESET_FAILED,
|
||||
INFRA_CONFIG_UPDATE_FAILED,
|
||||
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
|
||||
INFRA_CONFIG_OPERATION_NOT_ALLOWED,
|
||||
} from 'src/errors';
|
||||
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils';
|
||||
import {
|
||||
throwErr,
|
||||
validateSMTPEmail,
|
||||
validateSMTPUrl,
|
||||
validateUrl,
|
||||
} from 'src/utils';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper';
|
||||
import { ServiceStatus, getDefaultInfraConfigs, stopApp } from './helper';
|
||||
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
|
||||
import { AuthProvider } from 'src/auth/helper';
|
||||
|
||||
@@ -30,70 +32,32 @@ export class InfraConfigService implements OnModuleInit {
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
// Following fields are not updatable by `infraConfigs` Mutation. Use dedicated mutations for these fields instead.
|
||||
EXCLUDE_FROM_UPDATE_CONFIGS = [
|
||||
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
|
||||
InfraConfigEnum.ALLOW_ANALYTICS_COLLECTION,
|
||||
InfraConfigEnum.ANALYTICS_USER_ID,
|
||||
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
|
||||
];
|
||||
// Following fields can not be fetched by `infraConfigs` Query. Use dedicated queries for these fields instead.
|
||||
EXCLUDE_FROM_FETCH_CONFIGS = [
|
||||
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
|
||||
InfraConfigEnum.ANALYTICS_USER_ID,
|
||||
InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
|
||||
];
|
||||
|
||||
async onModuleInit() {
|
||||
await this.initializeInfraConfigTable();
|
||||
}
|
||||
|
||||
getDefaultInfraConfigs(): { name: InfraConfigEnum; value: string }[] {
|
||||
// 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.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the 'infra_config' table with values from .env
|
||||
* @description This function create rows 'infra_config' in very first time (only once)
|
||||
*/
|
||||
async initializeInfraConfigTable() {
|
||||
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
|
||||
const infraConfigDefaultObjs = this.getDefaultInfraConfigs();
|
||||
|
||||
// Check if all the 'names' are listed in the default values
|
||||
if (enumValues.length !== infraConfigDefaultObjs.length) {
|
||||
throw new Error(INFRA_CONFIG_NOT_LISTED);
|
||||
}
|
||||
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
|
||||
|
||||
// Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table
|
||||
const dbInfraConfigs = await this.prisma.infraConfig.findMany();
|
||||
@@ -147,12 +111,10 @@ export class InfraConfigService implements OnModuleInit {
|
||||
* Update InfraConfig by name
|
||||
* @param name Name of the InfraConfig
|
||||
* @param value Value of the InfraConfig
|
||||
* @param restartEnabled If true, restart the app after updating the InfraConfig
|
||||
* @returns InfraConfig model
|
||||
*/
|
||||
async update(
|
||||
name: InfraConfigEnumForClient | InfraConfigEnum,
|
||||
value: string,
|
||||
) {
|
||||
async update(name: InfraConfigEnum, value: string, restartEnabled = false) {
|
||||
const isValidate = this.validateEnvValues([{ name, value }]);
|
||||
if (E.isLeft(isValidate)) return E.left(isValidate.left);
|
||||
|
||||
@@ -162,7 +124,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
data: { value },
|
||||
});
|
||||
|
||||
stopApp();
|
||||
if (restartEnabled) stopApp();
|
||||
|
||||
return E.right(this.cast(infraConfig));
|
||||
} catch (e) {
|
||||
@@ -176,6 +138,11 @@ export class InfraConfigService implements OnModuleInit {
|
||||
* @returns InfraConfig model
|
||||
*/
|
||||
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);
|
||||
if (E.isLeft(isValidate)) return E.left(isValidate.left);
|
||||
|
||||
@@ -209,12 +176,26 @@ export class InfraConfigService implements OnModuleInit {
|
||||
) {
|
||||
switch (service) {
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
* @param provider Auth Provider to enable or disable
|
||||
@@ -261,6 +258,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
const isUpdated = await this.update(
|
||||
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
|
||||
updatedAuthProviders.join(','),
|
||||
true,
|
||||
);
|
||||
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
|
||||
|
||||
@@ -272,7 +270,7 @@ export class InfraConfigService implements OnModuleInit {
|
||||
* @param name Name of the InfraConfig
|
||||
* @returns InfraConfig model
|
||||
*/
|
||||
async get(name: InfraConfigEnumForClient) {
|
||||
async get(name: InfraConfigEnum) {
|
||||
try {
|
||||
const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({
|
||||
where: { name },
|
||||
@@ -289,7 +287,15 @@ export class InfraConfigService implements OnModuleInit {
|
||||
* @param names Names of the InfraConfigs
|
||||
* @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 {
|
||||
const infraConfigs = await this.prisma.infraConfig.findMany({
|
||||
where: { name: { in: names } },
|
||||
@@ -316,13 +322,24 @@ export class InfraConfigService implements OnModuleInit {
|
||||
*/
|
||||
async reset() {
|
||||
try {
|
||||
const infraConfigDefaultObjs = this.getDefaultInfraConfigs();
|
||||
const infraConfigDefaultObjs = await getDefaultInfraConfigs();
|
||||
|
||||
await this.prisma.infraConfig.deleteMany({
|
||||
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({
|
||||
data: infraConfigDefaultObjs,
|
||||
data: [
|
||||
...updatedInfraConfigDefaultObjs,
|
||||
{
|
||||
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
|
||||
value: 'true',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
stopApp();
|
||||
@@ -338,36 +355,60 @@ export class InfraConfigService implements OnModuleInit {
|
||||
*/
|
||||
validateEnvValues(
|
||||
infraConfigs: {
|
||||
name: InfraConfigEnumForClient | InfraConfigEnum;
|
||||
name: InfraConfigEnum;
|
||||
value: string;
|
||||
}[],
|
||||
) {
|
||||
for (let i = 0; i < infraConfigs.length; i++) {
|
||||
switch (infraConfigs[i].name) {
|
||||
case InfraConfigEnumForClient.MAILER_SMTP_URL:
|
||||
case InfraConfigEnum.MAILER_SMTP_URL:
|
||||
const isValidUrl = validateSMTPUrl(infraConfigs[i].value);
|
||||
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
break;
|
||||
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
|
||||
case InfraConfigEnum.MAILER_ADDRESS_FROM:
|
||||
const isValidEmail = validateSMTPEmail(infraConfigs[i].value);
|
||||
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
break;
|
||||
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
|
||||
case InfraConfigEnum.GOOGLE_CLIENT_ID:
|
||||
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
break;
|
||||
case InfraConfigEnumForClient.GOOGLE_CLIENT_SECRET:
|
||||
case InfraConfigEnum.GOOGLE_CLIENT_SECRET:
|
||||
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
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);
|
||||
break;
|
||||
case InfraConfigEnumForClient.GITHUB_CLIENT_SECRET:
|
||||
case InfraConfigEnum.GITHUB_CLIENT_ID:
|
||||
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
break;
|
||||
case InfraConfigEnumForClient.MICROSOFT_CLIENT_ID:
|
||||
case InfraConfigEnum.GITHUB_CLIENT_SECRET:
|
||||
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
|
||||
import { InfraConfigEnum } from 'src/types/InfraConfig';
|
||||
import { ServiceStatus } from './helper';
|
||||
import { AuthProvider } from 'src/auth/helper';
|
||||
|
||||
@InputType()
|
||||
export class InfraConfigArgs {
|
||||
@Field(() => InfraConfigEnumForClient, {
|
||||
@Field(() => InfraConfigEnum, {
|
||||
description: 'Infra Config Name',
|
||||
})
|
||||
name: InfraConfigEnumForClient;
|
||||
name: InfraConfigEnum;
|
||||
|
||||
@Field({
|
||||
description: 'Infra Config Value',
|
||||
|
||||
@@ -25,7 +25,7 @@ export class MailerService {
|
||||
): string {
|
||||
switch (mailDesc.template) {
|
||||
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':
|
||||
return 'Sign in to Hoppscotch';
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
color: #3869D4;
|
||||
}
|
||||
|
||||
a.nohighlight {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
@@ -458,7 +464,7 @@
|
||||
<td class="content-cell">
|
||||
<div class="f-fallback">
|
||||
<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 -->
|
||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
@@ -484,7 +490,7 @@
|
||||
Welcome aboard, <br />
|
||||
Your friends at Hoppscotch
|
||||
</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 -->
|
||||
<table class="body-sub">
|
||||
<tr>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<style type="text/css" rel="stylesheet" media="all">
|
||||
/* Base ------------------------------ */
|
||||
|
||||
|
||||
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");
|
||||
body {
|
||||
width: 100% !important;
|
||||
@@ -22,19 +22,25 @@
|
||||
margin: 0;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: #3869D4;
|
||||
}
|
||||
|
||||
|
||||
a.nohighlight {
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
td {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
|
||||
.preheader {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
@@ -47,13 +53,13 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
/* Type ------------------------------ */
|
||||
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: "Nunito Sans", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
@@ -61,7 +67,7 @@
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
@@ -69,7 +75,7 @@
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
color: #333333;
|
||||
@@ -77,12 +83,12 @@
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
td,
|
||||
th {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol,
|
||||
@@ -91,25 +97,25 @@
|
||||
font-size: 16px;
|
||||
line-height: 1.625;
|
||||
}
|
||||
|
||||
|
||||
p.sub {
|
||||
font-size: 13px;
|
||||
}
|
||||
/* Utilities ------------------------------ */
|
||||
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
/* Buttons ------------------------------ */
|
||||
|
||||
|
||||
.button {
|
||||
background-color: #3869D4;
|
||||
border-top: 10px solid #3869D4;
|
||||
@@ -124,7 +130,7 @@
|
||||
-webkit-text-size-adjust: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
.button--green {
|
||||
background-color: #22BC66;
|
||||
border-top: 10px solid #22BC66;
|
||||
@@ -132,7 +138,7 @@
|
||||
border-bottom: 10px solid #22BC66;
|
||||
border-left: 18px solid #22BC66;
|
||||
}
|
||||
|
||||
|
||||
.button--red {
|
||||
background-color: #FF6136;
|
||||
border-top: 10px solid #FF6136;
|
||||
@@ -140,7 +146,7 @@
|
||||
border-bottom: 10px solid #FF6136;
|
||||
border-left: 18px solid #FF6136;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
@@ -148,21 +154,21 @@
|
||||
}
|
||||
}
|
||||
/* Attribute list ------------------------------ */
|
||||
|
||||
|
||||
.attributes {
|
||||
margin: 0 0 21px;
|
||||
}
|
||||
|
||||
|
||||
.attributes_content {
|
||||
background-color: #F4F4F7;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
|
||||
.attributes_item {
|
||||
padding: 0;
|
||||
}
|
||||
/* Related Items ------------------------------ */
|
||||
|
||||
|
||||
.related {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -171,31 +177,31 @@
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
|
||||
.related_item {
|
||||
padding: 10px 0;
|
||||
color: #CBCCCF;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
|
||||
.related_item-title {
|
||||
display: block;
|
||||
margin: .5em 0 0;
|
||||
}
|
||||
|
||||
|
||||
.related_item-thumb {
|
||||
display: block;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.related_heading {
|
||||
border-top: 1px solid #CBCCCF;
|
||||
text-align: center;
|
||||
padding: 25px 0 10px;
|
||||
}
|
||||
/* Discount Code ------------------------------ */
|
||||
|
||||
|
||||
.discount {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -206,33 +212,33 @@
|
||||
background-color: #F4F4F7;
|
||||
border: 2px dashed #CBCCCF;
|
||||
}
|
||||
|
||||
|
||||
.discount_heading {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.discount_body {
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
}
|
||||
/* Social Icons ------------------------------ */
|
||||
|
||||
|
||||
.social {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
.social td {
|
||||
padding: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
||||
.social_icon {
|
||||
height: 20px;
|
||||
margin: 0 8px 10px 8px;
|
||||
padding: 0;
|
||||
}
|
||||
/* Data table ------------------------------ */
|
||||
|
||||
|
||||
.purchase {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -241,7 +247,7 @@
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
|
||||
.purchase_content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -250,50 +256,50 @@
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
|
||||
.purchase_item {
|
||||
padding: 10px 0;
|
||||
color: #51545E;
|
||||
font-size: 15px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
|
||||
.purchase_heading {
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
|
||||
.purchase_heading p {
|
||||
margin: 0;
|
||||
color: #85878E;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
.purchase_footer {
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
|
||||
.purchase_total {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
|
||||
.purchase_total--label {
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
background-color: #F2F4F6;
|
||||
color: #51545E;
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
color: #51545E;
|
||||
}
|
||||
|
||||
|
||||
.email-wrapper {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -303,7 +309,7 @@
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #F2F4F6;
|
||||
}
|
||||
|
||||
|
||||
.email-content {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -313,16 +319,16 @@
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
/* Masthead ----------------------- */
|
||||
|
||||
|
||||
.email-masthead {
|
||||
padding: 25px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.email-masthead_logo {
|
||||
width: 94px;
|
||||
}
|
||||
|
||||
|
||||
.email-masthead_name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
@@ -331,7 +337,7 @@
|
||||
text-shadow: 0 1px 0 white;
|
||||
}
|
||||
/* Body ------------------------------ */
|
||||
|
||||
|
||||
.email-body {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
@@ -340,7 +346,7 @@
|
||||
-premailer-cellpadding: 0;
|
||||
-premailer-cellspacing: 0;
|
||||
}
|
||||
|
||||
|
||||
.email-body_inner {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
@@ -350,7 +356,7 @@
|
||||
-premailer-cellspacing: 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
|
||||
.email-footer {
|
||||
width: 570px;
|
||||
margin: 0 auto;
|
||||
@@ -360,11 +366,11 @@
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.email-footer p {
|
||||
color: #A8AAAF;
|
||||
}
|
||||
|
||||
|
||||
.body-action {
|
||||
width: 100%;
|
||||
margin: 30px auto;
|
||||
@@ -374,25 +380,25 @@
|
||||
-premailer-cellspacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.body-sub {
|
||||
margin-top: 25px;
|
||||
padding-top: 25px;
|
||||
border-top: 1px solid #EAEAEC;
|
||||
}
|
||||
|
||||
|
||||
.content-cell {
|
||||
padding: 45px;
|
||||
}
|
||||
/*Media Queries ------------------------------ */
|
||||
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-body_inner,
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body,
|
||||
.email-body,
|
||||
|
||||
@@ -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 {}
|
||||
58
packages/hoppscotch-backend/src/posthog/posthog.service.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
14
packages/hoppscotch-backend/src/team-collection/helper.ts
Normal 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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { GqlCollectionTeamMemberGuard } from './guards/gql-collection-team-membe
|
||||
import { TeamModule } from '../team/team.module';
|
||||
import { UserModule } from '../user/user.module';
|
||||
import { PubSubModule } from '../pubsub/pubsub.module';
|
||||
import { TeamCollectionController } from './team-collection.controller';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, TeamModule, UserModule, PubSubModule],
|
||||
@@ -15,5 +16,6 @@ import { PubSubModule } from '../pubsub/pubsub.module';
|
||||
GqlCollectionTeamMemberGuard,
|
||||
],
|
||||
exports: [TeamCollectionService, GqlCollectionTeamMemberGuard],
|
||||
controllers: [TeamCollectionController],
|
||||
})
|
||||
export class TeamCollectionModule {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { HttpStatus, Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { TeamCollection } from './team-collection.model';
|
||||
import {
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
TEAM_COL_SAME_NEXT_COLL,
|
||||
TEAM_COL_REORDERING_FAILED,
|
||||
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';
|
||||
import { PubSubService } from '../pubsub/pubsub.service';
|
||||
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 { CollectionFolder } from 'src/types/CollectionFolder';
|
||||
import { stringToJson } from 'src/utils';
|
||||
import { CollectionSearchNode } from 'src/types/CollectionSearchNode';
|
||||
import { ParentTreeQueryReturnType, SearchQueryReturnType } from './helper';
|
||||
import { RESTError } from 'src/types/RESTError';
|
||||
|
||||
@Injectable()
|
||||
export class TeamCollectionService {
|
||||
@@ -1056,4 +1063,266 @@ export class TeamCollectionService {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
@@ -4,26 +4,23 @@ export enum InfraConfigEnum {
|
||||
|
||||
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
|
||||
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
|
||||
GOOGLE_CALLBACK_URL = 'GOOGLE_CALLBACK_URL',
|
||||
GOOGLE_SCOPE = 'GOOGLE_SCOPE',
|
||||
|
||||
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
|
||||
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
|
||||
GITHUB_CALLBACK_URL = 'GITHUB_CALLBACK_URL',
|
||||
GITHUB_SCOPE = 'GITHUB_SCOPE',
|
||||
|
||||
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
|
||||
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',
|
||||
}
|
||||
|
||||
export enum InfraConfigEnumForClient {
|
||||
MAILER_SMTP_URL = 'MAILER_SMTP_URL',
|
||||
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',
|
||||
|
||||
ALLOW_ANALYTICS_COLLECTION = 'ALLOW_ANALYTICS_COLLECTION',
|
||||
ANALYTICS_USER_ID = 'ANALYTICS_USER_ID',
|
||||
IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP',
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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
|
||||
*/
|
||||
export type AuthError = {
|
||||
export type RESTError = {
|
||||
message: string;
|
||||
statusCode: HttpStatus;
|
||||
};
|
||||
@@ -17,3 +17,21 @@ export class PaginationArgs {
|
||||
})
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -56,3 +56,22 @@ export enum SessionType {
|
||||
registerEnumType(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;
|
||||
}
|
||||
|
||||
@@ -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 { PrismaService } from 'src/prisma/prisma.service';
|
||||
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', () => {
|
||||
test('should successfully create user and account for magic-link given valid inputs', async () => {
|
||||
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', () => {
|
||||
test('should resolve right and return 20 users when cursor is null', async () => {
|
||||
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', () => {
|
||||
test('should return a list of admin users', async () => {
|
||||
mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers);
|
||||
@@ -556,4 +654,17 @@ describe('UserService', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,13 +8,14 @@ import * as T from 'fp-ts/Task';
|
||||
import * as A from 'fp-ts/Array';
|
||||
import { pipe, constVoid } from 'fp-ts/function';
|
||||
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 { USER_UPDATE_FAILED } from 'src/errors';
|
||||
import { PubSubService } from 'src/pubsub/pubsub.service';
|
||||
import { stringToJson, taskEitherValidateArraySeq } from 'src/utils';
|
||||
import { UserDataHandler } from './user.data.handler';
|
||||
import { User as DbUser } from '@prisma/client';
|
||||
import { OffsetPaginationArgs } from 'src/types/input-types.args';
|
||||
|
||||
@Injectable()
|
||||
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
|
||||
*
|
||||
@@ -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
|
||||
* @param sessionData string of the session
|
||||
@@ -286,6 +325,7 @@ export class UserService {
|
||||
* @param cursorID string of userUID or null
|
||||
* @param take number of users to query
|
||||
* @returns an array of `User` object
|
||||
* @deprecated use fetchAllUsersV2 instead
|
||||
*/
|
||||
async fetchAllUsers(cursorID: string, take: number) {
|
||||
const fetchedUsers = await this.prisma.user.findMany({
|
||||
@@ -296,6 +336,43 @@ export class UserService {
|
||||
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
|
||||
* @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
|
||||
* @returns an array of admin users
|
||||
@@ -444,4 +538,22 @@ export class UserService {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExecutionContext } from '@nestjs/common';
|
||||
import { ExecutionContext, HttpException } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { pipe } from 'fp-ts/lib/function';
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
JSON_INVALID,
|
||||
} from './errors';
|
||||
import { AuthProvider } from './auth/helper';
|
||||
import { RESTError } from './types/RESTError';
|
||||
|
||||
/**
|
||||
* A workaround to throw an exception in an expression.
|
||||
@@ -27,6 +28,15 @@ export function throwErr(errMessage: string): never {
|
||||
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.
|
||||
* Used for debugging functional pipelines.
|
||||
@@ -173,6 +183,16 @@ export const validateSMTPUrl = (url: string) => {
|
||||
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
|
||||
* @param {str} str The string to parse
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
// * The entry point of the CLI
|
||||
require("../dist").cli(process.argv);
|
||||
6
packages/hoppscotch-cli/bin/hopp.js
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
// * The entry point of the CLI
|
||||
|
||||
import { cli } from "../dist/index.js";
|
||||
|
||||
cli(process.argv);
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@hoppscotch/cli",
|
||||
"version": "0.5.1",
|
||||
"version": "0.6.0",
|
||||
"description": "A CLI to run Hoppscotch test scripts in CI environments.",
|
||||
"homepage": "https://hoppscotch.io",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"hopp": "bin/hopp"
|
||||
"hopp": "bin/hopp.js"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -39,27 +40,31 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"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": {
|
||||
"@hoppscotch/data": "workspace:^",
|
||||
"@hoppscotch/js-sandbox": "workspace:^",
|
||||
"@relmify/jest-fp-ts": "^2.1.1",
|
||||
"@swc/core": "^1.3.92",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/qs": "^6.9.8",
|
||||
"axios": "^0.21.4",
|
||||
"chalk": "^4.1.2",
|
||||
"commander": "^11.0.0",
|
||||
"esm": "^3.2.25",
|
||||
"fp-ts": "^2.16.1",
|
||||
"io-ts": "^2.2.20",
|
||||
"@swc/core": "^1.3.105",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/qs": "^6.9.11",
|
||||
"fp-ts": "^2.16.2",
|
||||
"jest": "^29.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier": "^3.2.4",
|
||||
"qs": "^6.11.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tsup": "^7.2.0",
|
||||
"typescript": "^5.2.2",
|
||||
"ts-jest": "^29.1.2",
|
||||
"tsup": "^8.0.1",
|
||||
"typescript": "^5.3.3",
|
||||
"verzod": "^0.2.2",
|
||||
"zod": "^3.22.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,138 +3,247 @@ import { ExecException } from "child_process";
|
||||
import { HoppErrorCode } from "../../types/errors";
|
||||
import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils";
|
||||
|
||||
describe("Test 'hopp test <file>' command:", () => {
|
||||
test("No collection file path provided.", async () => {
|
||||
const args = "test";
|
||||
const { stderr } = await runCLI(args);
|
||||
describe("Test `hopp test <file>` command:", () => {
|
||||
describe("Argument parsing", () => {
|
||||
test("Errors with the code `INVALID_ARGUMENT` for not supplying enough arguments", async () => {
|
||||
const args = "test";
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
|
||||
test("Collection file not found.", async () => {
|
||||
const args = "test notfound.json";
|
||||
const { stderr } = await runCLI(args);
|
||||
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>("FILE_NOT_FOUND");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
})
|
||||
|
||||
test("Collection file is invalid JSON.", async () => {
|
||||
const args = `test ${getTestJsonFilePath(
|
||||
"malformed-collection.json"
|
||||
)}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
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 { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
|
||||
});
|
||||
|
||||
test("Malformed collection file.", async () => {
|
||||
const args = `test ${getTestJsonFilePath(
|
||||
"malformed-collection2.json"
|
||||
)}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
test("Errors with the code UNKNOWN_ERROR if the supplied collection export file content isn't valid JSON", async () => {
|
||||
const args = `test ${getTestJsonFilePath("malformed-coll.json", "collection")}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR");
|
||||
});
|
||||
|
||||
test("Invalid arguement.", async () => {
|
||||
const args = "invalid-arg";
|
||||
const { stderr } = await runCLI(args);
|
||||
test("Errors with the code `MALFORMED_COLLECTION` if the supplied collection export file content is malformed", async () => {
|
||||
const args = `test ${getTestJsonFilePath("malformed-coll-2.json", "collection")}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
|
||||
});
|
||||
|
||||
test("Collection file not JSON type.", async () => {
|
||||
const args = `test ${getTestJsonFilePath("notjson.txt")}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
test("Errors with the code `INVALID_FILE_TYPE` if the supplied collection export file doesn't end with the `.json` extension", async () => {
|
||||
const args = `test ${getTestJsonFilePath("notjson-coll.txt", "collection")}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
|
||||
});
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
|
||||
});
|
||||
|
||||
test("Some errors occured (exit code 1).", async () => {
|
||||
const args = `test ${getTestJsonFilePath("fails.json")}`;
|
||||
const { error } = await runCLI(args);
|
||||
test("Fails if the collection file includes scripts with incorrect API usage and failed assertions", async () => {
|
||||
const args = `test ${getTestJsonFilePath("fails-coll.json", "collection")}`;
|
||||
const { error } = await runCLI(args);
|
||||
|
||||
expect(error).not.toBeNull();
|
||||
expect(error).toMatchObject(<ExecException>{
|
||||
code: 1,
|
||||
expect(error).not.toBeNull();
|
||||
expect(error).toMatchObject(<ExecException>{
|
||||
code: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("No errors occured (exit code 0).", async () => {
|
||||
const args = `test ${getTestJsonFilePath("passes.json")}`;
|
||||
test("Successfully processes a supplied collection export file of the expected format", async () => {
|
||||
const args = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
|
||||
const { error } = await runCLI(args);
|
||||
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
|
||||
test("Supports inheriting headers and authorization set at the root collection", async () => {
|
||||
const args = `test ${getTestJsonFilePath("collection-level-headers-auth.json")}`;
|
||||
test("Successfully inherits headers and authorization set at the root collection", async () => {
|
||||
const args = `test ${getTestJsonFilePath(
|
||||
"collection-level-headers-auth-coll.json", "collection"
|
||||
)}`;
|
||||
const { error } = await runCLI(args);
|
||||
|
||||
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:", () => {
|
||||
const VALID_TEST_ARGS = `test ${getTestJsonFilePath(
|
||||
"passes.json"
|
||||
)}`;
|
||||
describe("Test `hopp test <file> --env <file>` command:", () => {
|
||||
describe("Supplied environment export file validations", () => {
|
||||
const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
|
||||
|
||||
test("No env file path provided.", async () => {
|
||||
const args = `${VALID_TEST_ARGS} --env`;
|
||||
const { stderr } = await runCLI(args);
|
||||
test("Errors with the code `INVALID_ARGUMENT` if no file is supplied", async () => {
|
||||
const args = `${VALID_TEST_ARGS} --env`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
|
||||
});
|
||||
|
||||
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-coll.txt", "collection"
|
||||
)}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
|
||||
});
|
||||
|
||||
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 { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
|
||||
});
|
||||
|
||||
test("Errors with the code `MALFORMED_ENV_FILE` on supplying a malformed environment export file", async () => {
|
||||
const ENV_PATH = getTestJsonFilePath("malformed-envs.json", "environment");
|
||||
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("ENV file not JSON type.", async () => {
|
||||
const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath("notjson.txt")}`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
|
||||
});
|
||||
|
||||
test("ENV file not found.", async () => {
|
||||
const args = `${VALID_TEST_ARGS} --env notfound.json`;
|
||||
const { stderr } = await runCLI(args);
|
||||
|
||||
const out = getErrorCode(stderr);
|
||||
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
|
||||
});
|
||||
|
||||
test("No errors occured (exit code 0).", async () => {
|
||||
const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json");
|
||||
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json");
|
||||
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 { error } = await runCLI(args);
|
||||
expect(error).toBeNull();
|
||||
});
|
||||
|
||||
test("Correctly resolves environment variables referenced in the request body", async () => {
|
||||
const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json");
|
||||
const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json");
|
||||
test("Successfully resolves environment variables referenced in the request body", async () => {
|
||||
const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json", "collection");
|
||||
const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json", "environment");
|
||||
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
|
||||
|
||||
const { error } = await runCLI(args);
|
||||
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:", () => {
|
||||
const VALID_TEST_ARGS = `test ${getTestJsonFilePath(
|
||||
"passes.json"
|
||||
)}`;
|
||||
describe("Test `hopp test <file> --delay <delay_in_ms>` command:", () => {
|
||||
const VALID_TEST_ARGS = `test ${getTestJsonFilePath("passes-coll.json", "collection")}`;
|
||||
|
||||
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 { stderr } = await runCLI(args);
|
||||
|
||||
@@ -142,7 +251,7 @@ describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
|
||||
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 { stderr } = await runCLI(args);
|
||||
|
||||
@@ -150,10 +259,17 @@ describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
|
||||
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 { error } = await runCLI(args);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestD",
|
||||
"params": [],
|
||||
@@ -40,7 +40,8 @@
|
||||
"body": {
|
||||
"contentType": null,
|
||||
"body": null
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
@@ -52,7 +53,7 @@
|
||||
],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestC",
|
||||
"params": [],
|
||||
@@ -67,7 +68,8 @@
|
||||
"body": {
|
||||
"contentType": null,
|
||||
"body": null
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
],
|
||||
"auth": {
|
||||
@@ -88,7 +90,7 @@
|
||||
],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestB",
|
||||
"params": [],
|
||||
@@ -104,6 +106,7 @@
|
||||
"contentType": null,
|
||||
"body": null
|
||||
},
|
||||
"requestVariables": [],
|
||||
"id": "clpttpdq00003qp16kut6doqv"
|
||||
}
|
||||
],
|
||||
@@ -116,7 +119,7 @@
|
||||
],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestA",
|
||||
"params": [],
|
||||
@@ -132,6 +135,7 @@
|
||||
"contentType": null,
|
||||
"body": null
|
||||
},
|
||||
"requestVariables": [],
|
||||
"id": "clpttpdq00003qp16kut6doqv"
|
||||
}
|
||||
],
|
||||
@@ -158,7 +162,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestB",
|
||||
"params": [],
|
||||
@@ -174,6 +178,7 @@
|
||||
"contentType": null,
|
||||
"body": null
|
||||
},
|
||||
"requestVariables": [],
|
||||
"id": "clpttpdq00003qp16kut6doqv"
|
||||
}
|
||||
],
|
||||
@@ -186,7 +191,7 @@
|
||||
],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"name": "RequestA",
|
||||
"params": [],
|
||||
@@ -202,6 +207,7 @@
|
||||
"contentType": null,
|
||||
"body": null
|
||||
},
|
||||
"requestVariables": [],
|
||||
"id": "clpttpdq00003qp16kut6doqv"
|
||||
}
|
||||
],
|
||||
@@ -218,4 +224,4 @@
|
||||
"token": "BearerToken"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -4,7 +4,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "<<URL>>",
|
||||
"name": "test1",
|
||||
"params": [],
|
||||
@@ -16,7 +16,8 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n \"<<BODY_KEY>>\":\"<<BODY_VALUE>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
|
||||
"name": "",
|
||||
"params": [],
|
||||
@@ -23,10 +23,11 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": [],
|
||||
},
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.dio/<<HEADERS_TYPE2>>",
|
||||
"name": "success",
|
||||
"params": [],
|
||||
@@ -44,7 +45,8 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
{
|
||||
"v": 1,
|
||||
"folders": [],
|
||||
"requests":
|
||||
"requests":
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
|
||||
"name": "fail",
|
||||
"params": [],
|
||||
@@ -22,10 +22,11 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": [],
|
||||
},
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
|
||||
"name": "success",
|
||||
"params": [],
|
||||
@@ -43,7 +44,8 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
{
|
||||
"v": 1,
|
||||
"folders": [],
|
||||
"requests":
|
||||
"requests":
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
|
||||
"name": "fail",
|
||||
"params": [],
|
||||
@@ -22,7 +22,8 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE1>>",
|
||||
"name": "",
|
||||
"params": [],
|
||||
@@ -23,10 +23,11 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE1>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
},
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"endpoint": "https://echo.hoppscotch.io/<<HEADERS_TYPE2>>",
|
||||
"name": "success",
|
||||
"params": [],
|
||||
@@ -44,7 +45,8 @@
|
||||
"body": {
|
||||
"contentType": "application/json",
|
||||
"body": "{\n\"test\": \"<<HEADERS_TYPE2>>\"\n}"
|
||||
}
|
||||
},
|
||||
"requestVariables": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"folders": [],
|
||||
"requests": [
|
||||
{
|
||||
"v": "1",
|
||||
"v": "2",
|
||||
"name": "test-request",
|
||||
"endpoint": "https://echo.hoppscotch.io",
|
||||
"method": "POST",
|
||||
@@ -19,7 +19,8 @@
|
||||
"body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}"
|
||||
},
|
||||
"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": {
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": 123,
|
||||
"v": "1",
|
||||
"name": "secret-envs",
|
||||
"values": [
|
||||
{
|
||||
"key": "secretVar",
|
||||
"secret": true
|
||||
},
|
||||
{
|
||||
"key": "regularVar",
|
||||
"secret": false,
|
||||
"value": "regular-variable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"v": 0,
|
||||
"name": "Response body sample",
|
||||
"variables": [
|
||||
{
|
||||
@@ -34,4 +35,4 @@
|
||||
"value": "<<salutation>> <<fullName>>"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,13 +3,13 @@ import { resolve } from "path";
|
||||
|
||||
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 command = `node ${CLI_PATH} ${args}`
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
export const getTestJsonFilePath = (file: string) => {
|
||||
const filePath = resolve(__dirname, `../../src/__tests__/samples/${file}`);
|
||||
export const getTestJsonFilePath = (file: string, kind: "collection" | "environment") => {
|
||||
const kindDir = {
|
||||
collection: "collections",
|
||||
environment: "environments",
|
||||
}[kind];
|
||||
|
||||
const filePath = resolve(__dirname, `../../src/__tests__/samples/${kindDir}/${file}`);
|
||||
return filePath;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import chalk from "chalk";
|
||||
import { program } from "commander";
|
||||
import { Command } from "commander";
|
||||
import * as E from "fp-ts/Either";
|
||||
import { version } from "../package.json";
|
||||
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"
|
||||
)}`;
|
||||
|
||||
const program = new Command()
|
||||
|
||||
program
|
||||
.name("hopp")
|
||||
.version(version, "-v, --ver", "see the current version of hopp-cli")
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface RequestStack {
|
||||
*/
|
||||
export interface RequestConfig extends AxiosRequestConfig {
|
||||
supported: boolean;
|
||||
displayUrl?: string
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
effectiveFinalURL: string;
|
||||
effectiveFinalDisplayURL?: string;
|
||||
effectiveFinalHeaders: { key: string; value: string; active: boolean }[];
|
||||
effectiveFinalParams: { key: string; value: string; active: boolean }[];
|
||||
effectiveFinalBody: FormData | string | null;
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
import { Environment } from "@hoppscotch/data";
|
||||
import { entityReference } from "verzod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { error } from "../../types/errors";
|
||||
import {
|
||||
HoppEnvs,
|
||||
HoppEnvPair,
|
||||
HoppEnvKeyPairObject,
|
||||
HoppEnvExportObject,
|
||||
HoppBulkEnvExportObject,
|
||||
HoppEnvPair,
|
||||
HoppEnvs
|
||||
} from "../../types/request";
|
||||
import { readJsonFile } from "../../utils/mutators";
|
||||
|
||||
/**
|
||||
* Parses env json file for given path and validates the parsed env json object.
|
||||
* @param path Path of env.json file to be parsed.
|
||||
* @returns For successful parsing we get HoppEnvs 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
|
||||
* @returns For successful parsing we get HoppEnvs object
|
||||
*/
|
||||
export async function parseEnvsData(path: string) {
|
||||
const contents = await readJsonFile(path);
|
||||
const envPairs: Array<HoppEnvPair> = [];
|
||||
const HoppEnvKeyPairResult = HoppEnvKeyPairObject.safeParse(contents);
|
||||
const HoppEnvExportObjectResult = HoppEnvExportObject.safeParse(contents);
|
||||
const HoppBulkEnvExportObjectResult =
|
||||
HoppBulkEnvExportObject.safeParse(contents);
|
||||
const envPairs: Array<Environment["variables"][number] | HoppEnvPair> = [];
|
||||
|
||||
// CLI doesnt support bulk environments export.
|
||||
// Hence we check for this case and throw an error if it matches the format.
|
||||
// The legacy key-value pair format that is still supported
|
||||
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) {
|
||||
throw error({ code: "BULK_ENV_FILE", path, data: error });
|
||||
}
|
||||
|
||||
// Checks if the environment file is of the correct format.
|
||||
// If it doesnt match either of them, we throw an error.
|
||||
if (!(HoppEnvKeyPairResult.success || HoppEnvExportObjectResult.success)) {
|
||||
// Checks if the environment file is of the correct format
|
||||
// If it doesnt match either of them, we throw an error
|
||||
if (!HoppEnvKeyPairResult.success && HoppEnvExportObjectResult.type === "err") {
|
||||
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)) {
|
||||
envPairs.push({ key, value });
|
||||
}
|
||||
} else if (HoppEnvExportObjectResult.success) {
|
||||
envPairs.push(...HoppEnvExportObjectResult.data.variables);
|
||||
} else if (HoppEnvExportObjectResult.type === "ok") {
|
||||
envPairs.push(...HoppEnvExportObjectResult.value.variables);
|
||||
}
|
||||
|
||||
return <HoppEnvs>{ global: [], selected: envPairs };
|
||||
|
||||
@@ -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 { HoppCLIError } from "./errors";
|
||||
import { z } from "zod";
|
||||
|
||||
export type FormDataEntry = {
|
||||
key: string;
|
||||
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());
|
||||
|
||||
// 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 = {
|
||||
global: HoppEnvPair[];
|
||||
selected: HoppEnvPair[];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
|
||||
import { bold } from "chalk";
|
||||
import chalk from "chalk";
|
||||
import { log } from "console";
|
||||
import * as A from "fp-ts/Array";
|
||||
import { pipe } from "fp-ts/function";
|
||||
import round from "lodash/round";
|
||||
import { round } from "lodash-es";
|
||||
|
||||
import { CollectionRunnerParam } from "../types/collections";
|
||||
import {
|
||||
@@ -68,7 +68,7 @@ export const collectionsRunner = async (
|
||||
};
|
||||
|
||||
// Request processing initiated message.
|
||||
log(WARN(`\nRunning: ${bold(requestPath)}`));
|
||||
log(WARN(`\nRunning: ${chalk.bold(requestPath)}`));
|
||||
|
||||
// Processing current request.
|
||||
const result = await processRequest(processRequestParams)();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bold } from "chalk";
|
||||
import chalk from "chalk";
|
||||
import { groupEnd, group, log } from "console";
|
||||
import { handleError } from "../handlers/error";
|
||||
import { RequestConfig } from "../interfaces/request";
|
||||
@@ -120,7 +120,7 @@ export const printErrorsReport = (
|
||||
errorsReport: HoppCLIError[]
|
||||
) => {
|
||||
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);
|
||||
for (const errorReport of errorsReport) {
|
||||
@@ -143,7 +143,7 @@ export const printFailedTestsReport = (
|
||||
|
||||
// Only printing test-reports with failed test-cases.
|
||||
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);
|
||||
|
||||
for (const failedTestReport of failedTestsReport) {
|
||||
@@ -176,7 +176,7 @@ export const printRequestRunner = {
|
||||
*/
|
||||
start: (requestConfig: RequestConfig) => {
|
||||
const METHOD = BG_INFO(` ${requestConfig.method} `);
|
||||
const ENDPOINT = requestConfig.url;
|
||||
const ENDPOINT = requestConfig.displayUrl || requestConfig.url;
|
||||
|
||||
process.stdout.write(`${METHOD} ${ENDPOINT}`);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { clone } from "lodash";
|
||||
import { clone } from "lodash-es";
|
||||
|
||||
/**
|
||||
* Sorts the array based on the sort func.
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as E from "fp-ts/Either";
|
||||
import * as S from "fp-ts/string";
|
||||
import * as O from "fp-ts/Option";
|
||||
import { error } from "../types/errors";
|
||||
import round from "lodash/round";
|
||||
import { round } from "lodash-es";
|
||||
import { DEFAULT_DURATION_PRECISION } from "./constants";
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,10 @@ import { toFormData } from "./mutators";
|
||||
export const preRequestScriptRunner = (
|
||||
request: HoppRESTRequest,
|
||||
envs: HoppEnvs
|
||||
): TE.TaskEither<HoppCLIError, EffectiveHoppRESTRequest> =>
|
||||
): TE.TaskEither<
|
||||
HoppCLIError,
|
||||
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
|
||||
> =>
|
||||
pipe(
|
||||
TE.of(request),
|
||||
TE.chain(({ preRequestScript }) =>
|
||||
@@ -68,7 +71,10 @@ export const preRequestScriptRunner = (
|
||||
export function getEffectiveRESTRequest(
|
||||
request: HoppRESTRequest,
|
||||
environment: Environment
|
||||
): E.Either<HoppCLIError, EffectiveHoppRESTRequest> {
|
||||
): E.Either<
|
||||
HoppCLIError,
|
||||
{ effectiveRequest: EffectiveHoppRESTRequest } & { updatedEnvs: HoppEnvs }
|
||||
> {
|
||||
const envVariables = environment.variables;
|
||||
|
||||
// Parsing final headers with applied ENVs.
|
||||
@@ -162,12 +168,30 @@ export function getEffectiveRESTRequest(
|
||||
}
|
||||
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({
|
||||
...request,
|
||||
effectiveFinalURL,
|
||||
effectiveFinalHeaders,
|
||||
effectiveFinalParams,
|
||||
effectiveFinalBody,
|
||||
effectiveRequest: {
|
||||
...request,
|
||||
effectiveFinalURL,
|
||||
effectiveFinalDisplayURL,
|
||||
effectiveFinalHeaders,
|
||||
effectiveFinalParams,
|
||||
effectiveFinalBody,
|
||||
},
|
||||
updatedEnvs: { global: [], selected: envVariables },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
|
||||
import { Environment, HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
|
||||
import axios, { Method } from "axios";
|
||||
import * as A from "fp-ts/Array";
|
||||
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
|
||||
|
||||
/**
|
||||
* 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
|
||||
* perform HTTP request.
|
||||
@@ -38,6 +70,7 @@ import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test";
|
||||
export const createRequest = (req: EffectiveHoppRESTRequest): RequestConfig => {
|
||||
const config: RequestConfig = {
|
||||
supported: true,
|
||||
displayUrl: req.effectiveFinalDisplayURL
|
||||
};
|
||||
const { finalBody, finalEndpoint, finalHeaders, finalParams } = getRequest;
|
||||
const reqParams = finalParams(req);
|
||||
@@ -221,9 +254,13 @@ export const processRequest =
|
||||
effectiveFinalParams: [],
|
||||
effectiveFinalURL: "",
|
||||
};
|
||||
let updatedEnvs = <HoppEnvs>{};
|
||||
|
||||
// Fetch values for secret environment variables from system environment
|
||||
const processedEnvs = processEnvs(envs)
|
||||
|
||||
// Executing pre-request-script
|
||||
const preRequestRes = await preRequestScriptRunner(request, envs)();
|
||||
const preRequestRes = await preRequestScriptRunner(request, processedEnvs)();
|
||||
if (E.isLeft(preRequestRes)) {
|
||||
printPreRequestRunner.fail();
|
||||
|
||||
@@ -231,8 +268,8 @@ export const processRequest =
|
||||
report.errors.push(preRequestRes.left);
|
||||
report.result = report.result && false;
|
||||
} else {
|
||||
// Updating effective-request
|
||||
effectiveRequest = preRequestRes.right;
|
||||
// Updating effective-request and consuming updated envs after pre-request script execution
|
||||
({ effectiveRequest, updatedEnvs } = preRequestRes.right);
|
||||
}
|
||||
|
||||
// Creating request-config for request-runner.
|
||||
@@ -270,7 +307,7 @@ export const processRequest =
|
||||
const testScriptParams = getTestScriptParams(
|
||||
_requestRunnerRes,
|
||||
request,
|
||||
envs
|
||||
updatedEnvs
|
||||
);
|
||||
|
||||
// Executing test-runner.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"outDir": ".",
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
|
||||
@@ -3,17 +3,14 @@ import { defineConfig } from "tsup";
|
||||
export default defineConfig({
|
||||
entry: [ "./src/index.ts" ],
|
||||
outDir: "./dist/",
|
||||
format: ["cjs"],
|
||||
format: ["esm"],
|
||||
platform: "node",
|
||||
sourcemap: true,
|
||||
bundle: true,
|
||||
target: "node12",
|
||||
target: "esnext",
|
||||
skipNodeModulesBundle: false,
|
||||
esbuildOptions(options) {
|
||||
options.bundle = true
|
||||
},
|
||||
noExternal: [
|
||||
/\w+/
|
||||
],
|
||||
clean: true,
|
||||
});
|
||||
|
||||
@@ -429,6 +429,11 @@ pre.ace_editor {
|
||||
}
|
||||
}
|
||||
|
||||
.splitpanes__pane {
|
||||
@apply will-change-auto;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.smart-splitter .splitpanes__splitter {
|
||||
@apply relative;
|
||||
@apply before:absolute;
|
||||
@@ -558,12 +563,22 @@ details[open] summary .indicator {
|
||||
.env-highlight {
|
||||
@apply text-accentContrast;
|
||||
|
||||
&.env-found {
|
||||
@apply bg-accentDark;
|
||||
@apply hover:bg-accent;
|
||||
&.request-variable-highlight {
|
||||
@apply bg-amber-500;
|
||||
@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 hover:bg-red-600;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"choose_file": "选择文件",
|
||||
"clear": "清除",
|
||||
"clear_all": "全部清除",
|
||||
"clear_history": "Clear all History",
|
||||
"clear_history": "清除全部历史记录",
|
||||
"close": "关闭",
|
||||
"connect": "连接",
|
||||
"connecting": "连接中",
|
||||
@@ -35,7 +35,7 @@
|
||||
"prettify": "美化",
|
||||
"properties": "Properties",
|
||||
"remove": "移除",
|
||||
"rename": "Rename",
|
||||
"rename": "重命名",
|
||||
"restore": "恢复",
|
||||
"save": "保存",
|
||||
"scroll_to_bottom": "滚动至底部",
|
||||
@@ -86,8 +86,8 @@
|
||||
"search": "搜索",
|
||||
"share": "分享",
|
||||
"shortcuts": "快捷方式",
|
||||
"social_description": "Follow us on social media to stay updated with the latest news, updates and releases.",
|
||||
"social_links": "Social links",
|
||||
"social_description": "在社交媒体上关注我们,了解最新新闻、更新和发布。",
|
||||
"social_links": "社交媒体链接",
|
||||
"spotlight": "聚光灯",
|
||||
"status": "状态",
|
||||
"status_description": "检查网站状态",
|
||||
@@ -121,7 +121,7 @@
|
||||
"generate_token": "生成令牌",
|
||||
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
|
||||
"include_in_url": "包含在 URL 内",
|
||||
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
|
||||
"inherited_from": "Inherited {auth} from parent collection {collection} ",
|
||||
"learn": "了解更多",
|
||||
"oauth": {
|
||||
"redirect_auth_server_returned_error": "Auth Server returned an error state",
|
||||
@@ -162,15 +162,15 @@
|
||||
"renamed": "集合已更名",
|
||||
"request_in_use": "请求正在使用中",
|
||||
"save_as": "另存为",
|
||||
"save_to_collection": "Save to Collection",
|
||||
"save_to_collection": "保存至集合",
|
||||
"select": "选择一个集合",
|
||||
"select_location": "选择位置",
|
||||
"select_team": "选择一个团队",
|
||||
"team_collections": "团队集合"
|
||||
},
|
||||
"confirm": {
|
||||
"close_unsaved_tab": "Are you sure you want to close this tab?",
|
||||
"close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.",
|
||||
"close_unsaved_tab": "你确定要关闭此标签页吗?",
|
||||
"close_unsaved_tabs": "你确定要关闭所有标签页吗? {count} 个未保存的标签页将被丢失。",
|
||||
"exit_team": "你确定要离开此团队吗?",
|
||||
"logout": "你确定要登出吗?",
|
||||
"remove_collection": "你确定要永久删除该集合吗?",
|
||||
@@ -186,9 +186,9 @@
|
||||
"sync": "您确定要同步该工作区吗?"
|
||||
},
|
||||
"context_menu": {
|
||||
"add_parameters": "Add to parameters",
|
||||
"open_request_in_new_tab": "Open request in new tab",
|
||||
"set_environment_variable": "Set as variable"
|
||||
"add_parameters": "添加至参数",
|
||||
"open_request_in_new_tab": "在新标签页中打开请求",
|
||||
"set_environment_variable": "设置为变量"
|
||||
},
|
||||
"cookies": {
|
||||
"modal": {
|
||||
@@ -244,7 +244,7 @@
|
||||
"team_name": "团队名称为空",
|
||||
"teams": "团队为空",
|
||||
"tests": "没有针对该请求的测试",
|
||||
"shortcodes": "Shortcodes 为空"
|
||||
"shortcodes": "短链接为空"
|
||||
},
|
||||
"environment": {
|
||||
"add_to_global": "添加到全局环境",
|
||||
@@ -252,32 +252,32 @@
|
||||
"create_new": "创建新环境",
|
||||
"created": "环境已创建",
|
||||
"deleted": "环境已删除",
|
||||
"duplicated": "Environment duplicated",
|
||||
"duplicated": "环境已复制",
|
||||
"edit": "编辑环境",
|
||||
"empty_variables": "No variables",
|
||||
"global": "Global",
|
||||
"global_variables": "Global variables",
|
||||
"empty_variables": "没有变量",
|
||||
"global": "全局",
|
||||
"global_variables": "全局变量",
|
||||
"import_or_create": "Import or create a environment",
|
||||
"invalid_name": "请提供有效的环境名称",
|
||||
"list": "Environment variables",
|
||||
"list": "环境变量",
|
||||
"my_environments": "我的环境",
|
||||
"name": "Name",
|
||||
"name": "名称",
|
||||
"nested_overflow": "环境嵌套深度超过限制(10层)",
|
||||
"new": "新建环境",
|
||||
"no_active_environment": "No active environment",
|
||||
"no_active_environment": "没有激活的环境",
|
||||
"no_environment": "无环境",
|
||||
"no_environment_description": "没有选择环境。选择如何处理以下变量。",
|
||||
"quick_peek": "Environment Quick Peek",
|
||||
"replace_with_variable": "Replace with variable",
|
||||
"scope": "Scope",
|
||||
"quick_peek": "快速浏览环境",
|
||||
"replace_with_variable": "替换为变量",
|
||||
"scope": "范围",
|
||||
"select": "选择环境",
|
||||
"set": "Set environment",
|
||||
"set_as_environment": "Set as environment",
|
||||
"set": "设置环境",
|
||||
"set_as_environment": "设置为环境",
|
||||
"team_environments": "团队环境",
|
||||
"title": "环境",
|
||||
"updated": "环境已更新",
|
||||
"value": "Value",
|
||||
"variable": "Variable",
|
||||
"value": "值",
|
||||
"variable": "变量",
|
||||
"variable_list": "变量列表"
|
||||
},
|
||||
"error": {
|
||||
@@ -296,6 +296,7 @@
|
||||
"incorrect_email": "电子邮箱错误",
|
||||
"invalid_link": "无效链接",
|
||||
"invalid_link_description": "你点击的链接无效或已过期。",
|
||||
"invalid_embed_link": "The embed does not exist or is invalid.",
|
||||
"json_parsing_failed": "不合法的 JSON",
|
||||
"json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试",
|
||||
"network_error": "好像发生了网络错误,请重试。",
|
||||
@@ -306,7 +307,7 @@
|
||||
"no_results_found": "找不到结果",
|
||||
"page_not_found": "找不到此頁面",
|
||||
"please_install_extension": "Please install the extension and add origin to the extension.",
|
||||
"proxy_error": "Proxy error",
|
||||
"proxy_error": "代理错误",
|
||||
"script_fail": "无法执行预请求脚本",
|
||||
"something_went_wrong": "发生了一些错误",
|
||||
"test_script_fail": "无法执行请求脚本"
|
||||
@@ -314,10 +315,13 @@
|
||||
"export": {
|
||||
"as_json": "导出为 JSON",
|
||||
"create_secret_gist": "创建私密 Gist",
|
||||
"create_secret_gist_tooltip_text": "Export as secret Gist",
|
||||
"failed": "Something went wrong while exporting",
|
||||
"gist_created": "已创建 Gist",
|
||||
"secret_gist_success": "Successfully exported as secret Gist",
|
||||
"require_github": "使用 GitHub 登录以创建私密 Gist",
|
||||
"title": "导出"
|
||||
"title": "导出",
|
||||
"success": "Successfully exported",
|
||||
"gist_created": "已创建 Gist"
|
||||
},
|
||||
"filter": {
|
||||
"all": "全部",
|
||||
@@ -333,13 +337,13 @@
|
||||
"renamed": "文件夹已更名"
|
||||
},
|
||||
"graphql": {
|
||||
"connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?",
|
||||
"connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is",
|
||||
"connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is",
|
||||
"connection_switch_confirm": "您想连接最新的 GraphQL 端点吗?",
|
||||
"connection_switch_new_url": "切换到标签页将使您与活动的 GraphQL 连接断开。新的连接 URL 是",
|
||||
"connection_switch_url": "您已连接到 GraphQL 端点,连接 URL 为",
|
||||
"mutations": "变更",
|
||||
"schema": "模式",
|
||||
"subscriptions": "订阅",
|
||||
"switch_connection": "Switch connection"
|
||||
"switch_connection": "切换连接"
|
||||
},
|
||||
"graphql_collections": {
|
||||
"title": "GraphQL Collections"
|
||||
@@ -408,27 +412,27 @@
|
||||
"title": "导入"
|
||||
},
|
||||
"inspections": {
|
||||
"description": "Inspect possible errors",
|
||||
"description": "查可能的错误",
|
||||
"environment": {
|
||||
"add_environment": "Add to Environment",
|
||||
"not_found": "Environment variable “{environment}” not found."
|
||||
"add_environment": "添加到环境",
|
||||
"not_found": "环境变量“{environment}”未找到。"
|
||||
},
|
||||
"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": {
|
||||
"401_error": "Please check your authentication credentials.",
|
||||
"404_error": "Please check your request URL and method type.",
|
||||
"cors_error": "Please check your Cross-Origin Resource Sharing configuration.",
|
||||
"default_error": "Please check your request.",
|
||||
"network_error": "Please check your network connection."
|
||||
"401_error": "请检查您的身份验证凭据。",
|
||||
"404_error": "请检查您的请求 URL 和方法类型。",
|
||||
"cors_error": "请检查您的跨源资源共享配置。",
|
||||
"default_error": "请检查您的请求。",
|
||||
"network_error": "请检查您的网络连接。"
|
||||
},
|
||||
"title": "Inspector",
|
||||
"url": {
|
||||
"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.",
|
||||
"extention_enable_action": "Enable Browser Extension",
|
||||
"extention_not_enabled": "Extension not enabled."
|
||||
"extension_not_installed": "未安装扩展。",
|
||||
"extension_unknown_origin": "确保您已将 API 端点的源添加到 Hoppscotch 浏览器扩展列表中。",
|
||||
"extention_enable_action": "启用浏览器扩展",
|
||||
"extention_not_enabled": "扩展未启用。"
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
@@ -460,10 +464,10 @@
|
||||
"invalid_topic": "请提供该订阅的主题",
|
||||
"keep_alive": "Keep Alive",
|
||||
"log": "日志",
|
||||
"lw_message": "Last-Will Message",
|
||||
"lw_qos": "Last-Will QoS",
|
||||
"lw_retain": "Last-Will Retain",
|
||||
"lw_topic": "Last-Will Topic",
|
||||
"lw_message": "遗嘱消息",
|
||||
"lw_qos": "遗嘱消息QoS",
|
||||
"lw_retain": "遗嘱消息保留",
|
||||
"lw_topic": "遗嘱消息主题",
|
||||
"message": "消息",
|
||||
"new": "新订阅",
|
||||
"not_connected": "请先启动MQTT连接。",
|
||||
@@ -544,7 +548,7 @@
|
||||
"payload": "负载",
|
||||
"query": "查询",
|
||||
"raw_body": "原始请求体",
|
||||
"rename": "Rename Request",
|
||||
"rename": "重命名请求",
|
||||
"renamed": "请求重命名",
|
||||
"run": "运行",
|
||||
"save": "保存",
|
||||
@@ -553,7 +557,7 @@
|
||||
"share": "分享",
|
||||
"share_description": "分享 Hoppscotch 给你的朋友",
|
||||
"share_request": "Share Request",
|
||||
"stop": "Stop",
|
||||
"stop": "停止",
|
||||
"title": "请求",
|
||||
"type": "请求类型",
|
||||
"url": "URL",
|
||||
@@ -684,26 +688,26 @@
|
||||
"title": "导航"
|
||||
},
|
||||
"others": {
|
||||
"prettify": "Prettify Editor's Content",
|
||||
"title": "Others"
|
||||
"prettify": "美化内容",
|
||||
"title": "其他"
|
||||
},
|
||||
"request": {
|
||||
"delete_method": "选择 DELETE 方法",
|
||||
"get_method": "选择 GET 方法",
|
||||
"head_method": "选择 HEAD 方法",
|
||||
"import_curl": "Import cURL",
|
||||
"import_curl": "导入cURL",
|
||||
"method": "方法",
|
||||
"next_method": "选择下一个方法",
|
||||
"post_method": "选择 POST 方法",
|
||||
"previous_method": "选择上一个方法",
|
||||
"put_method": "选择 PUT 方法",
|
||||
"rename": "Rename Request",
|
||||
"rename": "重命名请求",
|
||||
"reset_request": "重置请求",
|
||||
"save_request": "Save Request",
|
||||
"save_request": "保存请求",
|
||||
"save_to_collections": "保存到集合",
|
||||
"send_request": "发送请求",
|
||||
"share_request": "Share Request",
|
||||
"show_code": "Generate code snippet",
|
||||
"show_code": "生成代码片段",
|
||||
"title": "请求",
|
||||
"copy_request_link": "复制请求链接"
|
||||
},
|
||||
@@ -735,82 +739,82 @@
|
||||
"url": "URL"
|
||||
},
|
||||
"spotlight": {
|
||||
"change_language": "Change Language",
|
||||
"change_language": "更改语言",
|
||||
"environments": {
|
||||
"delete": "Delete current environment",
|
||||
"duplicate": "Duplicate current environment",
|
||||
"duplicate_global": "Duplicate global environment",
|
||||
"edit": "Edit current environment",
|
||||
"edit_global": "Edit global environment",
|
||||
"new": "Create new environment",
|
||||
"new_variable": "Create a new environment variable",
|
||||
"title": "Environments"
|
||||
"delete": "删除当前环境",
|
||||
"duplicate": "复制当前环境",
|
||||
"duplicate_global": "复制全局环境",
|
||||
"edit": "编辑当前环境",
|
||||
"edit_global": "编辑全局环境",
|
||||
"new": "创建新环境",
|
||||
"new_variable": "创建新的环境变量",
|
||||
"title": "环境"
|
||||
},
|
||||
"general": {
|
||||
"chat": "Chat with support",
|
||||
"help_menu": "Help and support",
|
||||
"open_docs": "Read Documentation",
|
||||
"open_github": "Open GitHub repository",
|
||||
"open_keybindings": "Keyboard shortcuts",
|
||||
"social": "Social",
|
||||
"title": "General"
|
||||
"chat": "与支持人员聊天",
|
||||
"help_menu": "帮助和支持",
|
||||
"open_docs": "阅读文档",
|
||||
"open_github": "打开 GitHub 存储库",
|
||||
"open_keybindings": "键盘快捷键",
|
||||
"social": "社交媒体",
|
||||
"title": "一般"
|
||||
},
|
||||
"graphql": {
|
||||
"connect": "Connect to server",
|
||||
"disconnect": "Disconnect from server"
|
||||
"connect": "连接到服务器",
|
||||
"disconnect": "与服务器断开连接"
|
||||
},
|
||||
"miscellaneous": {
|
||||
"invite": "Invite your friends to Hoppscotch",
|
||||
"title": "Miscellaneous"
|
||||
"invite": "邀请你的朋友来 Hoppscotch",
|
||||
"title": "杂项"
|
||||
},
|
||||
"request": {
|
||||
"save_as_new": "Save as new request",
|
||||
"select_method": "Select method",
|
||||
"switch_to": "Switch to",
|
||||
"tab_authorization": "Authorization tab",
|
||||
"tab_body": "Body tab",
|
||||
"tab_headers": "Headers tab",
|
||||
"tab_parameters": "Parameters tab",
|
||||
"tab_pre_request_script": "Pre-request script tab",
|
||||
"tab_query": "Query tab",
|
||||
"tab_tests": "Tests tab",
|
||||
"tab_variables": "Variables tab"
|
||||
"save_as_new": "另存为新请求",
|
||||
"select_method": "选择方法",
|
||||
"switch_to": "切换到",
|
||||
"tab_authorization": "授权标签页",
|
||||
"tab_body": "请求体标签页",
|
||||
"tab_headers": "请求头标签页",
|
||||
"tab_parameters": "参数标签页",
|
||||
"tab_pre_request_script": "预请求脚本标签页",
|
||||
"tab_query": "查询标签页",
|
||||
"tab_tests": "测试标签页b",
|
||||
"tab_variables": "变量标签页"
|
||||
},
|
||||
"response": {
|
||||
"copy": "Copy response",
|
||||
"download": "Download response as file",
|
||||
"title": "Response"
|
||||
"copy": "复制响应",
|
||||
"download": "将响应下载为文件",
|
||||
"title": "响应"
|
||||
},
|
||||
"section": {
|
||||
"interceptor": "Interceptor",
|
||||
"interface": "Interface",
|
||||
"theme": "Theme",
|
||||
"user": "User"
|
||||
"interceptor": "拦截器",
|
||||
"interface": "界面",
|
||||
"theme": "主题",
|
||||
"user": "用户"
|
||||
},
|
||||
"settings": {
|
||||
"change_interceptor": "Change Interceptor",
|
||||
"change_language": "Change Language",
|
||||
"change_interceptor": "更改拦截器",
|
||||
"change_language": "更改语言",
|
||||
"theme": {
|
||||
"black": "Black",
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
"system": "System preference"
|
||||
"black": "黑色",
|
||||
"dark": "暗色",
|
||||
"light": "亮色",
|
||||
"system": "系统"
|
||||
}
|
||||
},
|
||||
"tab": {
|
||||
"close_current": "Close current tab",
|
||||
"close_others": "Close all other tabs",
|
||||
"duplicate": "Duplicate current tab",
|
||||
"new_tab": "Open a new tab",
|
||||
"title": "Tabs"
|
||||
"close_current": "关闭当前标签页",
|
||||
"close_others": "关闭所有其他标签页",
|
||||
"duplicate": "复制当前标签页",
|
||||
"new_tab": "打开新的标签页",
|
||||
"title": "标签页"
|
||||
},
|
||||
"workspace": {
|
||||
"delete": "Delete current team",
|
||||
"edit": "Edit current team",
|
||||
"invite": "Invite people to team",
|
||||
"new": "Create new team",
|
||||
"switch_to_personal": "Switch to your personal workspace",
|
||||
"title": "Teams"
|
||||
"delete": "删除当前团队",
|
||||
"edit": "编辑当前团队",
|
||||
"invite": "邀请人员加入团队",
|
||||
"new": "创建新团队",
|
||||
"switch_to_personal": "切换到您的个人工作空间",
|
||||
"title": "团队"
|
||||
}
|
||||
},
|
||||
"sse": {
|
||||
@@ -983,10 +987,10 @@
|
||||
"shortcodes": {
|
||||
"actions": "操作",
|
||||
"created_on": "创建于",
|
||||
"deleted": "已刪除快捷键",
|
||||
"deleted": "已刪除短链接",
|
||||
"method": "方法",
|
||||
"not_found": "找不到快捷键",
|
||||
"short_code": "快捷键",
|
||||
"not_found": "找不到短链接",
|
||||
"short_code": "短链接",
|
||||
"url": "URL"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"go_back": "Go back",
|
||||
"go_forward": "Go forward",
|
||||
"group_by": "Group by",
|
||||
"hide_secret": "Hide secret",
|
||||
"label": "Label",
|
||||
"learn_more": "Learn more",
|
||||
"less": "Less",
|
||||
@@ -43,6 +44,7 @@
|
||||
"search": "Search",
|
||||
"send": "Send",
|
||||
"share": "Share",
|
||||
"show_secret": "Show secret",
|
||||
"start": "Start",
|
||||
"starting": "Starting",
|
||||
"stop": "Stop",
|
||||
@@ -237,7 +239,9 @@
|
||||
"pending_invites": "There are no pending invites for this team",
|
||||
"profile": "Login to view your profile",
|
||||
"protocols": "Protocols are empty",
|
||||
"request_variables": "This request does not have any request variables",
|
||||
"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_logout": "Login to view your shared requests or create a new one",
|
||||
"subscription": "Subscriptions are empty",
|
||||
@@ -269,6 +273,8 @@
|
||||
"quick_peek": "Environment Quick Peek",
|
||||
"replace_with_variable": "Replace with variable",
|
||||
"scope": "Scope",
|
||||
"secrets": "Secrets",
|
||||
"secret_value": "Secret value",
|
||||
"select": "Select environment",
|
||||
"set": "Set environment",
|
||||
"set_as_environment": "Set as environment",
|
||||
@@ -277,6 +283,7 @@
|
||||
"updated": "Environment updated",
|
||||
"value": "Value",
|
||||
"variable": "Variable",
|
||||
"variables":"Variables",
|
||||
"variable_list": "Variable List"
|
||||
},
|
||||
"error": {
|
||||
@@ -309,7 +316,8 @@
|
||||
"proxy_error": "Proxy error",
|
||||
"script_fail": "Could not execute pre-request script",
|
||||
"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": {
|
||||
"as_json": "Export as JSON",
|
||||
@@ -407,12 +415,17 @@
|
||||
"json_description": "Import collections from a Hoppscotch Collections JSON file",
|
||||
"postman_environment": "Postman Environment",
|
||||
"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": {
|
||||
"description": "Inspect possible errors",
|
||||
"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."
|
||||
},
|
||||
"header": {
|
||||
@@ -548,6 +561,7 @@
|
||||
"raw_body": "Raw Request Body",
|
||||
"rename": "Rename Request",
|
||||
"renamed": "Request renamed",
|
||||
"request_variables": "Request variables",
|
||||
"run": "Run",
|
||||
"save": "Save",
|
||||
"save_as": "Save as",
|
||||
@@ -889,6 +903,7 @@
|
||||
"query": "Query",
|
||||
"schema": "Schema",
|
||||
"shared_requests": "Shared Requests",
|
||||
"share_tab_request": "Share tab request",
|
||||
"socketio": "Socket.IO",
|
||||
"sse": "SSE",
|
||||
"tests": "Tests",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"action": {
|
||||
"add": "Add",
|
||||
"add": "Добавить",
|
||||
"autoscroll": "Автоскрол",
|
||||
"cancel": "Отменить",
|
||||
"choose_file": "Выберите файл",
|
||||
@@ -121,7 +121,7 @@
|
||||
"generate_token": "Сгенерировать токен",
|
||||
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
|
||||
"include_in_url": "Добавить в URL",
|
||||
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
|
||||
"inherited_from": "Inherited {auth} from parent collection {collection} ",
|
||||
"learn": "Узнать больше",
|
||||
"oauth": {
|
||||
"redirect_auth_server_returned_error": "Auth Server returned an error state",
|
||||
@@ -148,7 +148,7 @@
|
||||
"created": "Коллекция создана",
|
||||
"different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией",
|
||||
"edit": "Редактировать коллекцию",
|
||||
"import_or_create": "Import or create a collection",
|
||||
"import_or_create": "Вы можете импортировать существующую или создать новую коллекцию",
|
||||
"invalid_name": "Укажите допустимое название коллекции",
|
||||
"invalid_root_move": "Коллекция уже в корне",
|
||||
"moved": "Перемещено успешно",
|
||||
@@ -170,7 +170,7 @@
|
||||
},
|
||||
"confirm": {
|
||||
"close_unsaved_tab": "Вы уверены что хотите закрыть эту вкладку?",
|
||||
"close_unsaved_tabs": "ВЫ уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.",
|
||||
"close_unsaved_tabs": "Вы уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.",
|
||||
"exit_team": "Вы точно хотите покинуть эту команду?",
|
||||
"logout": "Вы действительно хотите выйти?",
|
||||
"remove_collection": "Вы уверены, что хотите навсегда удалить эту коллекцию?",
|
||||
@@ -192,20 +192,20 @@
|
||||
},
|
||||
"cookies": {
|
||||
"modal": {
|
||||
"cookie_expires": "Expires",
|
||||
"cookie_name": "Name",
|
||||
"cookie_path": "Path",
|
||||
"cookie_string": "Cookie string",
|
||||
"cookie_value": "Value",
|
||||
"empty_domain": "Domain is empty",
|
||||
"empty_domains": "Domain list is empty",
|
||||
"enter_cookie_string": "Enter cookie string",
|
||||
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
|
||||
"cookie_expires": "Истекает",
|
||||
"cookie_name": "Имя",
|
||||
"cookie_path": "Путь",
|
||||
"cookie_string": "Cookie параметры",
|
||||
"cookie_value": "Значение",
|
||||
"empty_domain": "Нужно заполнить домен",
|
||||
"empty_domains": "Список доменов пуст",
|
||||
"enter_cookie_string": "Введите cookie параметры сюда",
|
||||
"interceptor_no_support": "Ваш текущий Перехватчик не поддерживает работу с cookie. Выберите другой Перехватчик и попробуйте еще раз.",
|
||||
"managed_tab": "Managed",
|
||||
"new_domain_name": "New domain name",
|
||||
"no_cookies_in_domain": "No cookies set for this domain",
|
||||
"new_domain_name": "Добавить новый домен",
|
||||
"no_cookies_in_domain": "Никаких cookie не установлено для этого домена",
|
||||
"raw_tab": "Raw",
|
||||
"set": "Set a cookie"
|
||||
"set": "Установить cookie"
|
||||
}
|
||||
},
|
||||
"count": {
|
||||
@@ -257,14 +257,14 @@
|
||||
"empty_variables": "No variables",
|
||||
"global": "Global",
|
||||
"global_variables": "Global variables",
|
||||
"import_or_create": "Import or create a environment",
|
||||
"import_or_create": "Импортировать или создать новое окружение",
|
||||
"invalid_name": "Укажите допустимое имя для окружения",
|
||||
"list": "Environment variables",
|
||||
"list": "Переменные окружения",
|
||||
"my_environments": "Мои окружения",
|
||||
"name": "Name",
|
||||
"nested_overflow": "максимальный уровень вложения переменных окружения - 10",
|
||||
"new": "Новая среда",
|
||||
"no_active_environment": "No active environment",
|
||||
"no_active_environment": "Нет активных окружений",
|
||||
"no_environment": "Нет окружения",
|
||||
"no_environment_description": "Не выбрано окружение, выберите что делать с переменными.",
|
||||
"quick_peek": "Environment Quick Peek",
|
||||
@@ -284,7 +284,7 @@
|
||||
"authproviders_load_error": "Unable to load auth providers",
|
||||
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
|
||||
"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 неправильно отформатирован",
|
||||
"danger_zone": "Опасная зона",
|
||||
"delete_account": "Вы являетесь владельцем этой команды:",
|
||||
@@ -296,16 +296,17 @@
|
||||
"incorrect_email": "Не корректный Email",
|
||||
"invalid_link": "Не корректная ссылка",
|
||||
"invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.",
|
||||
"invalid_embed_link": "The embed does not exist or is invalid.",
|
||||
"json_parsing_failed": "Не корректный JSON",
|
||||
"json_prettify_invalid_body": "Не удалось определить недопустимое тело, устранить синтаксические ошибки json и повторить попытку.",
|
||||
"network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.",
|
||||
"network_fail": "Не удалось отправить запрос",
|
||||
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
|
||||
"no_collections_to_export": "Нечего экспортировать. Для начала нужно создать коллекцию.",
|
||||
"no_duration": "Без продолжительности",
|
||||
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
|
||||
"no_environments_to_export": "Нечего экспортировать. Для начала нужно создать переменные окружения.",
|
||||
"no_results_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",
|
||||
"script_fail": "Не удалось выполнить сценарий предварительного запроса",
|
||||
"something_went_wrong": "Что-то пошло не так",
|
||||
@@ -314,10 +315,13 @@
|
||||
"export": {
|
||||
"as_json": "Экспорт как JSON",
|
||||
"create_secret_gist": "Создать секретный Gist",
|
||||
"create_secret_gist_tooltip_text": "Export as secret Gist",
|
||||
"failed": "Something went wrong while exporting",
|
||||
"gist_created": "Gist создан",
|
||||
"secret_gist_success": "Successfully exported as secret Gist",
|
||||
"require_github": "Войдите через GitHub, чтобы создать секретную суть",
|
||||
"title": "Экспорт"
|
||||
"title": "Экспорт",
|
||||
"success": "Successfully exported",
|
||||
"gist_created": "Gist создан"
|
||||
},
|
||||
"filter": {
|
||||
"all": "Все",
|
||||
@@ -375,7 +379,7 @@
|
||||
},
|
||||
"import": {
|
||||
"collections": "Импортировать коллекции",
|
||||
"curl": "Импортировать cURL",
|
||||
"curl": "Импортировать из cURL",
|
||||
"environments_from_gist": "Import From Gist",
|
||||
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
|
||||
"failed": "Ошибка импорта",
|
||||
@@ -523,10 +527,10 @@
|
||||
"different_collection": "Нельзя изменять порядок запросов из разных коллекций",
|
||||
"duplicated": "Запрос скопирован",
|
||||
"duration": "Продолжительность",
|
||||
"enter_curl": "Введите cURL",
|
||||
"enter_curl": "Введите сюда команду cURL",
|
||||
"generate_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",
|
||||
"header_list": "Список заголовков",
|
||||
"invalid_name": "Укажите имя для запроса",
|
||||
@@ -544,7 +548,7 @@
|
||||
"payload": "Полезная нагрузка",
|
||||
"query": "Запрос",
|
||||
"raw_body": "Необработанное тело запроса",
|
||||
"rename": "Rename Request",
|
||||
"rename": "Переименость запрос",
|
||||
"renamed": "Запрос переименован",
|
||||
"run": "Запустить",
|
||||
"save": "Сохранить",
|
||||
@@ -691,13 +695,13 @@
|
||||
"delete_method": "Выберите метод DELETE",
|
||||
"get_method": "Выберите метод GET",
|
||||
"head_method": "Выберите метод HEAD",
|
||||
"import_curl": "Import cURL",
|
||||
"import_curl": "Импортировать из cURL",
|
||||
"method": "Методика",
|
||||
"next_method": "Выберите следующий метод",
|
||||
"post_method": "Выберите метод POST",
|
||||
"previous_method": "Выбрать предыдущий метод",
|
||||
"put_method": "Выберите метод PUT",
|
||||
"rename": "Rename Request",
|
||||
"rename": "Переименовать запрос",
|
||||
"reset_request": "Сбросить запрос",
|
||||
"save_request": "Сохарнить запрос",
|
||||
"save_to_collections": "Сохранить в коллекции",
|
||||
@@ -874,8 +878,8 @@
|
||||
"tab": {
|
||||
"authorization": "Авторизация",
|
||||
"body": "Тело",
|
||||
"close": "Close Tab",
|
||||
"close_others": "Close other Tabs",
|
||||
"close": "Закрыть вкладку",
|
||||
"close_others": "Закрыть остальные вкладки",
|
||||
"collections": "Коллекции",
|
||||
"documentation": "Документация",
|
||||
"duplicate": "Duplicate Tab",
|
||||
@@ -905,7 +909,7 @@
|
||||
"email_do_not_match": "Электронная почта, которой Вы воспользовались не соответсвует указанной в данных Вашей учетной записи.",
|
||||
"exit": "Выйти из команды",
|
||||
"exit_disabled": "Только владелец не может выйти из команды",
|
||||
"failed_invites": "Failed invites",
|
||||
"failed_invites": "Непринятые приглашения",
|
||||
"invalid_coll_id": "Не верный идентификатор коллекции",
|
||||
"invalid_email_format": "Формат электронной почты недействителен",
|
||||
"invalid_id": "Некорректный ID команды. Свяжитесь с руководителем команды.",
|
||||
@@ -947,7 +951,7 @@
|
||||
"same_target_destination": "Таже цель и конечная точка",
|
||||
"saved": "Команда сохранена",
|
||||
"select_a_team": "Выбрать команду",
|
||||
"success_invites": "Success invites",
|
||||
"success_invites": "Принятые приглашения",
|
||||
"title": "Команды",
|
||||
"we_sent_invite_link": "Мы отправили все приглашения!",
|
||||
"we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду."
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@hoppscotch/common",
|
||||
"private": true,
|
||||
"version": "2023.12.1-1",
|
||||
"version": "2023.12.6",
|
||||
"scripts": {
|
||||
"dev": "pnpm exec npm-run-all -p -l dev:*",
|
||||
"test": "vitest --run",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<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="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 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 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="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="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="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="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="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="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="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="#575757"/>
|
||||
<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.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.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.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.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.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.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.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>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -1,19 +1,19 @@
|
||||
<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="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="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="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="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="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="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="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="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="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.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="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.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="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" stroke="#525252" 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="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="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="#0E0E0E"/>
|
||||
<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.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="#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="#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="#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="#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="#575757"/>
|
||||
<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.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.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.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="#0E0E0E"/>
|
||||
<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="#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.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>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -1,26 +1,28 @@
|
||||
<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="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="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="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="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="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="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 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 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 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 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 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="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="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="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 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 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 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 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 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="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.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="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="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="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="#0E0E0E"/>
|
||||
<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="#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="#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="#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.643L121.099 107.854Z" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
|
||||
<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 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 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 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 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="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="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 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 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 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 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 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="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 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.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="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="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>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -1,37 +1,37 @@
|
||||
<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="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="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="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="#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="#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.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="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.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="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="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="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="#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="#575757"/>
|
||||
<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.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.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="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.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="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="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="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="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="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="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="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="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="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.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="#575757"/>
|
||||
<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_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="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
|
||||
<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="#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_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="#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="#545454"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="58.2299" y1="30.8211" x2="58.2299" y2="28.0409" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#181818"/>
|
||||
<stop offset="0.9913" stop-color="#222427"/>
|
||||
<linearGradient id="paint0_linear_592_1141" x1="58.2301" y1="30.8211" x2="58.2301" y2="28.0409" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#151515"/>
|
||||
<stop offset="0.9913" stop-color="#0B0B0B"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="95.163" y1="107.023" x2="95.163" y2="104.243" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#181818"/>
|
||||
<stop offset="0.9913" stop-color="#222427"/>
|
||||
<linearGradient id="paint1_linear_592_1141" x1="95.163" y1="107.023" x2="95.163" y2="104.243" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#151515"/>
|
||||
<stop offset="0.9913" stop-color="#0B0B0B"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear" x1="21.3956" y1="106.915" x2="21.3956" y2="105.428" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#181818"/>
|
||||
<stop offset="0.9913" stop-color="#222427"/>
|
||||
<linearGradient id="paint2_linear_592_1141" x1="21.3956" y1="106.915" x2="21.3956" y2="105.428" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#151515"/>
|
||||
<stop offset="0.9913" stop-color="#0B0B0B"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,24 +1,24 @@
|
||||
<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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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 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="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="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 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="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="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="#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="#575757"/>
|
||||
<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="#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="#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="#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="#0E0E0E" stroke="#575757" 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.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.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="#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="#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="#0E0E0E" stroke="#575757" 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="#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="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
|
||||
<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="#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="#545454"/>
|
||||
<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.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>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
@@ -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 |
@@ -1,22 +1,22 @@
|
||||
<svg width="144" height="118" viewBox="0 0 144 118" 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="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="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="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"/>
|
||||
<circle cx="70.6176" 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="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.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="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="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 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.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="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"/>
|
||||
<rect x="41.5205" y="53.9714" width="29.097" 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="#737373"/>
|
||||
<rect x="45.9971" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/>
|
||||
<rect x="50.4736" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/>
|
||||
<rect x="54.9497" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#737373"/>
|
||||
<rect x="58.6802" 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="#737373"/>
|
||||
<svg width="144" height="117" viewBox="0 0 144 117" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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="#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="#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.901 73.9607 133.901 76.6481C133.901 79.3356 136.035 81.467 138.726 81.467Z" fill="#0E0E0E"/>
|
||||
<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="#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="#0E0E0E" stroke="#575757" stroke-width="2" stroke-miterlimit="10"/>
|
||||
<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="#575757"/>
|
||||
<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.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.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="#545454"/>
|
||||
<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="#545454"/>
|
||||
<rect x="45.9971" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
|
||||
<rect x="50.4734" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
|
||||
<rect x="54.9498" y="70.385" width="2.23823" height="2.23823" rx="1.11911" fill="#545454"/>
|
||||
<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="#545454"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -1,37 +1,38 @@
|
||||
<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="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.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.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.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="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="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="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="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="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="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="M1 103.569H138.853" stroke="#3c3c3c" 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="M127.366 51.0537V98.4062C127.366 101.356 124.917 103.569 122.009 103.569H97.8257V51.0537H127.366Z" fill="#DAE1ED"/>
|
||||
<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="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="#181818" stroke="#3c3c3c" 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 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="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="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 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="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="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.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.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.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.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.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.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="#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="#7F7F7F"/>
|
||||
<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.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="#575757" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" 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.365 51.0537V98.4062C127.365 101.356 124.916 103.569 122.008 103.569H97.8254V51.0537H127.365Z" fill="#EAEAEA"/>
|
||||
<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="#D6D6D6"/>
|
||||
<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.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.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="#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="#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="#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="#575757"/>
|
||||
<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 offset="1" stop-color="#F53689"/>
|
||||
<stop offset="0.0001" stop-color="#AAAAAA"/>
|
||||
<stop offset="1" stop-color="#3B3B3B"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear" x1="86.3762" y1="58.0033" x2="92.0807" y2="58.0033" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#83A6FF"/>
|
||||
<stop offset="1" stop-color="#5A78FF"/>
|
||||
<linearGradient id="paint1_linear_592_1292" x1="86.376" y1="58.0033" x2="92.0804" y2="58.0033" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#8D8D8D"/>
|
||||
</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="1"/>
|
||||
</linearGradient>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -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 |
@@ -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 |
@@ -1,17 +1,17 @@
|
||||
<svg width="128" height="111" viewBox="0 0 128 111" 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="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="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="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="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="M75.9185 5.11914V24.4727C75.9185 27.9435 78.8576 30.7201 82.1974 30.7201H100.924" fill="#525252"/>
|
||||
<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="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="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="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.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.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.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="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="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"/>
|
||||
<svg width="128" height="110" viewBox="0 0 128 110" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<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 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 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 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 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.9182 4.11914V23.4727C75.9182 26.9435 78.8573 29.7201 82.1972 29.7201H100.924" fill="#575757"/>
|
||||
<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.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.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.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.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.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.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.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.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>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -1,23 +1,23 @@
|
||||
<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="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="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="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="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="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="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="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="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="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 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 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="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="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="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="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="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="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 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 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="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.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="#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="#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="#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="#575757"/>
|
||||
<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.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.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.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="#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.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="#0E0E0E" stroke="#575757" stroke-width="1.5905" stroke-miterlimit="10" stroke-linejoin="round"/>
|
||||
<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.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="#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.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="#0E0E0E" stroke="#575757" stroke-width="1.5905" 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.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.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.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>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |