Compare commits

..

10 Commits

Author SHA1 Message Date
nivedin
01200c9e5f fix: duplicate tab reference bug 2023-09-18 11:11:16 +05:30
Yuri Grand
75193a7aa8 i18n: translate locales to russian (#3312)
chore: translate locales to russian
2023-09-13 12:53:10 +05:30
tyo
b269c239d9 chore(i18n): update translation for Indonesian (#3284) 2023-09-13 12:48:46 +05:30
Liyas Thomas
72b4a1fc4e fix: typo in "twitter link" and "invite to hoppscotch" action (#3346) 2023-09-13 11:55:58 +05:30
DNT
d2d1674d31 i18n: update vi.json (#3241) 2023-09-13 11:52:37 +05:30
Joel Jacob Stephen
a6b57777e3 refactor: remove font sizes from the app (#3341)
* refactor: remove font size from settings

* refactor: remove font size from themes

* refactor: remove font size from spotlight

* refactor: remove default font size

* chore: clean up

---------

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2023-09-13 11:45:38 +05:30
Joel Jacob Stephen
65ef4db86f refactor: remove zen mode from the app (#3337)
* refactor: remove zen mode from settings

* refactor: remove zen mode from footer and options
2023-09-12 14:10:38 +05:30
Nivedin
7201147b55 fix: context-menu position fixed while scrolling (#3340) 2023-09-12 12:43:10 +05:30
Anwarul Islam
dd143c95a9 fix: unusual behavior while scrolling through spotlight entries (#3324)
* fix: spotlight scroll issue

* fix: entry hidden issue

* chore: back to loop mode
2023-09-12 12:42:44 +05:30
James George
005581ee7d fix: broken link to REST API Testing docs (#3333)
fix: broken link to REST API Testing docs
2023-09-12 12:32:10 +05:30
36 changed files with 1038 additions and 1894 deletions

View File

@@ -20,7 +20,7 @@ services:
- PORT=3170
volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target.
- ./packages/hoppscotch-backend/:/usr/src/app/packages/hoppscotch-backend
# - ./packages/hoppscotch-backend/:/usr/src/app
- /usr/src/app/node_modules/
depends_on:
hoppscotch-db:

View File

@@ -21,8 +21,7 @@
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"do-test": "pnpm run test",
"seed": "node --loader ts-node/esm prisma/seed.ts"
"do-test": "pnpm run test"
},
"dependencies": {
"@nestjs-modules/mailer": "^1.8.1",
@@ -58,7 +57,6 @@
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"passport-microsoft": "^1.0.0",
"pg": "^8.11.3",
"prisma": "^4.16.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",

View File

@@ -1,2 +0,0 @@
-- CreateIndex
CREATE INDEX "TeamMember_userUid_idx" ON "TeamMember"("userUid");

View File

@@ -26,7 +26,6 @@ model TeamMember {
team Team @relation(fields: [teamID], references: [id], onDelete: Cascade)
@@unique([teamID, userUid])
@@index([userUid])
}
model TeamInvitation {

View File

@@ -1,58 +0,0 @@
import { PrismaClient, TeamMemberRole } from '@prisma/client';
const prisma = new PrismaClient();
const noOfUsers = 600000;
const getAllUser = async () => {
const users = await prisma.user.findMany();
return users;
};
const createUsers = async () => {
for (let i = 1; i <= noOfUsers; i++) {
try {
await prisma.user.create({
data: {
email: `${i}@gmail.com`,
},
});
} catch (_) {}
}
};
const createTeams = async () => {
const users = await getAllUser();
for (let i = 0; i < users.length; i++) {
try {
await prisma.team.create({
data: {
name: `Team ${i + 1}`,
members: {
create: {
userUid: users[i].uid,
role: TeamMemberRole.OWNER,
},
},
},
});
} catch (_) {}
}
};
async function main() {
console.log('Seeding...');
await createUsers();
await createTeams();
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});

View File

@@ -20,7 +20,6 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
import { COOKIES_NOT_FOUND } from './errors';
import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller';
import { DbModule } from './db/db.module';
@Module({
imports: [
@@ -67,7 +66,6 @@ import { DbModule } from './db/db.module';
ttl: +process.env.RATE_LIMIT_TTL,
limit: +process.env.RATE_LIMIT_MAX,
}),
DbModule,
UserModule,
AuthModule,
AdminModule,

View File

@@ -1 +0,0 @@
export const PG_CONNECTION = 'PG_CONNECTION';

View File

@@ -1,21 +0,0 @@
import { Global, Module } from '@nestjs/common';
import { Pool } from 'pg';
import { PG_CONNECTION } from 'src/constants';
const dbProvider = {
provide: PG_CONNECTION,
useValue: new Pool({
user: 'postgres',
host: 'hoppscotch-db',
database: 'hoppscotch',
password: 'testpass',
port: 5432,
}),
};
@Global()
@Module({
providers: [dbProvider],
exports: [dbProvider],
})
export class DbModule {}

View File

@@ -7,11 +7,7 @@ export class PrismaService
implements OnModuleInit, OnModuleDestroy
{
constructor() {
super(
{
log: ['query', 'info', 'warn', 'error'],
}
);
super();
}
async onModuleInit() {
await this.$connect();

View File

@@ -22,7 +22,6 @@ import { throwErr } from 'src/utils';
import { AuthUser } from 'src/types/AuthUser';
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
import { SkipThrottle } from '@nestjs/throttler';
import { cons } from 'fp-ts/lib/ReadonlyNonEmptyArray';
@UseGuards(GqlThrottlerGuard)
@Resolver(() => Team)
@@ -56,13 +55,8 @@ export class TeamResolver {
description: 'Returns the list of members of a team',
complexity: 10,
})
async teamMembers(@Parent() team: Team): Promise<TeamMember[]> {
const startR = Date.now();
const members = await this.teamService.getTeamMembers(team.id);
const endR = Date.now();
console.log('response generation: (teamMembers)', endR - startR, 'ms');
return members;
teamMembers(@Parent() team: Team): Promise<TeamMember[]> {
return this.teamService.getTeamMembers(team.id);
}
@ResolveField(() => TeamMemberRole, {
@@ -70,61 +64,41 @@ export class TeamResolver {
nullable: true,
})
@UseGuards(GqlAuthGuard)
async myRole(
myRole(
@Parent() team: Team,
@GqlUser() user: AuthUser,
): Promise<TeamMemberRole | null> {
const startR = Date.now();
const role = await this.teamService.getRoleOfUserInTeam(team.id, user.uid);
const endR = Date.now();
console.log('response generation: (myRole)', endR - startR, 'ms');
return role;
return this.teamService.getRoleOfUserInTeam(team.id, user.uid);
}
@ResolveField(() => Int, {
description: 'The number of users with the OWNER role in the team',
})
async ownersCount(@Parent() team: Team): Promise<number> {
const startR = Date.now();
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
ownersCount(@Parent() team: Team): Promise<number> {
return this.teamService.getCountOfUsersWithRoleInTeam(
team.id,
TeamMemberRole.OWNER,
);
const endR = Date.now();
console.log('response generation: (ownersCount)', endR - startR, 'ms');
return count;
}
@ResolveField(() => Int, {
description: 'The number of users with the EDITOR role in the team',
})
async editorsCount(@Parent() team: Team): Promise<number> {
const startR = Date.now();
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
editorsCount(@Parent() team: Team): Promise<number> {
return this.teamService.getCountOfUsersWithRoleInTeam(
team.id,
TeamMemberRole.EDITOR,
);
const endR = Date.now();
console.log('response generation: (editorsCount)', endR - startR, 'ms');
return count;
}
@ResolveField(() => Int, {
description: 'The number of users with the VIEWER role in the team',
})
async viewersCount(@Parent() team: Team): Promise<number> {
const startR = Date.now();
const count = await this.teamService.getCountOfUsersWithRoleInTeam(
viewersCount(@Parent() team: Team): Promise<number> {
return this.teamService.getCountOfUsersWithRoleInTeam(
team.id,
TeamMemberRole.VIEWER,
);
const endR = Date.now();
console.log('response generation: (viewersCount)', endR - startR, 'ms');
return count;
}
// Query
@@ -132,7 +106,7 @@ export class TeamResolver {
description: 'List of teams that the executing user belongs to.',
})
@UseGuards(GqlAuthGuard)
async myTeams(
myTeams(
@GqlUser() user: AuthUser,
@Args({
name: 'cursor',
@@ -143,15 +117,7 @@ export class TeamResolver {
})
cursor?: string,
): Promise<Team[]> {
const startR = Date.now();
const teams = await this.teamService.getTeamsOfUser(
user.uid,
cursor ?? null,
);
const endR = Date.now();
console.log('response generation: (myTeams)', endR - startR, 'ms');
return teams;
return this.teamService.getTeamsOfUser(user.uid, cursor ?? null);
}
@Query(() => Team, {
@@ -164,7 +130,7 @@ export class TeamResolver {
TeamMemberRole.EDITOR,
TeamMemberRole.OWNER,
)
async team(
team(
@Args({
name: 'teamID',
type: () => ID,
@@ -172,12 +138,7 @@ export class TeamResolver {
})
teamID: string,
): Promise<Team | null> {
const startR = Date.now();
const team = await this.teamService.getTeamWithID(teamID);
const endR = Date.now();
console.log('response generation: (team)', endR - startR, 'ms');
return team;
return this.teamService.getTeamWithID(teamID);
}
// Mutation
@@ -190,11 +151,7 @@ export class TeamResolver {
@Args({ name: 'name', description: 'Displayed name of the team' })
name: string,
): Promise<Team> {
const startR = Date.now();
const team = await this.teamService.createTeam(name, user.uid);
const endR = Date.now();
console.log('response generation: (createTeam)', endR - startR, 'ms');
if (E.isLeft(team)) throwErr(team.left);
return team.right;
}
@@ -212,11 +169,7 @@ export class TeamResolver {
})
teamID: string,
): Promise<boolean> {
const startR = Date.now();
const isUserLeft = await this.teamService.leaveTeam(teamID, user.uid);
const endR = Date.now();
console.log('response generation: (leaveTeam)', endR - startR, 'ms');
if (E.isLeft(isUserLeft)) throwErr(isUserLeft.left);
return isUserLeft.right;
}
@@ -241,11 +194,7 @@ export class TeamResolver {
})
userUid: string,
): Promise<boolean> {
const startR = Date.now();
const isRemoved = await this.teamService.leaveTeam(teamID, userUid);
const endR = Date.now();
console.log('response generation: (removeTeamMember)', endR - startR, 'ms');
if (E.isLeft(isRemoved)) throwErr(isRemoved.left);
return isRemoved.right;
}
@@ -261,11 +210,7 @@ export class TeamResolver {
@Args({ name: 'newName', description: 'The updated name of the team' })
newName: string,
): Promise<Team> {
const startR = Date.now();
const team = await this.teamService.renameTeam(teamID, newName);
const endR = Date.now();
console.log('response generation: (renameTeam)', endR - startR, 'ms');
if (E.isLeft(team)) throwErr(team.left);
return team.right;
}
@@ -279,11 +224,7 @@ export class TeamResolver {
@Args({ name: 'teamID', description: 'ID of the team', type: () => ID })
teamID: string,
): Promise<boolean> {
const startR = Date.now();
const isDeleted = await this.teamService.deleteTeam(teamID);
const endR = Date.now();
console.log('response generation: (deleteTeam)', endR - startR, 'ms');
if (E.isLeft(isDeleted)) throwErr(isDeleted.left);
return isDeleted.right;
}
@@ -313,19 +254,11 @@ export class TeamResolver {
})
newRole: TeamMemberRole,
): Promise<TeamMember> {
const startR = Date.now();
const teamMember = await this.teamService.updateTeamMemberRole(
teamID,
userUid,
newRole,
);
const endR = Date.now();
console.log(
'response generation: (updateTeamMemberRole)',
endR - startR,
'ms',
);
if (E.isLeft(teamMember)) throwErr(teamMember.left);
return teamMember.right;
}

View File

@@ -1,4 +1,4 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { Injectable, OnModuleInit } from '@nestjs/common';
import { TeamMember, TeamMemberRole, Team } from './team.model';
import { PrismaService } from '../prisma/prisma.service';
import { TeamMember as DbTeamMember } from '@prisma/client';
@@ -23,8 +23,6 @@ import * as T from 'fp-ts/Task';
import * as A from 'fp-ts/Array';
import { throwErr } from 'src/utils';
import { AuthUser } from '../types/AuthUser';
import { PG_CONNECTION } from 'src/constants';
import { Client } from 'pg';
@Injectable()
export class TeamService implements UserDataHandler, OnModuleInit {
@@ -32,11 +30,8 @@ export class TeamService implements UserDataHandler, OnModuleInit {
private readonly prisma: PrismaService,
private readonly userService: UserService,
private readonly pubsub: PubSubService,
@Inject(PG_CONNECTION) private conn: Client,
) {}
enableRawSql: boolean = false;
onModuleInit() {
this.userService.registerUserDataHandler(this);
}
@@ -57,37 +52,12 @@ export class TeamService implements UserDataHandler, OnModuleInit {
teamID: string,
role: TeamMemberRole,
): Promise<number> {
if (this.enableRawSql) {
const startQ = Date.now();
const count = await this.conn.query(
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${role}';`,
);
const endQ = Date.now();
console.log(
'getCountOfUsersWithRoleInTeam >>>>>>>>>>',
endQ - startQ,
'ms >>>>>',
count.rows,
);
return count.rows[0].count;
}
const startQ = Date.now();
const count = await this.prisma.teamMember.count({
return await this.prisma.teamMember.count({
where: {
teamID,
role,
},
});
const endQ = Date.now();
console.log(
'getCountOfUsersWithRoleInTeam >>>>>>>>>>',
endQ - startQ,
'ms >>>>>',
count,
);
return count;
}
async addMemberToTeamWithEmail(
@@ -107,11 +77,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
uid: string,
role: TeamMemberRole,
): Promise<TeamMember> {
const tm = await this.conn.query(
`INSERT INTO "TeamMember" (id, userUid, teamID, role) VALUES ('${new Date().toISOString()}', '${uid}', '${teamID}', '${role}') RETURNING *;`,
);
console.log('addMemberToTeam >>>>>>>>>>', tm.rows[0]);
const teamMember = await this.prisma.teamMember.create({
data: {
userUid: uid,
@@ -136,31 +101,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
}
async deleteTeam(teamID: string): Promise<E.Left<string> | E.Right<boolean>> {
if (this.enableRawSql) {
const startQ = Date.now();
const t = await this.conn.query(
`SELECT * FROM "Team" WHERE "id" = '${teamID}'`,
);
if (t.rows.length === 0) return E.left(TEAM_INVALID_ID);
await this.conn.query(
`DELETE FROM "TeamMember" WHERE "teamID" = '${teamID}' RETURNING *`,
);
await this.conn.query(`DELETE FROM "Team" WHERE "id" = '${teamID}'`);
const endQ = Date.now();
console.log(
'deleteTeam >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
t.rows[0],
);
return E.right(true);
}
const startQ = Date.now();
const team = await this.prisma.team.findUnique({
where: {
id: teamID,
@@ -179,8 +119,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
id: teamID,
},
});
const endQ = Date.now();
console.log('deleteTeam >>>>>>>>>>', endQ - startQ, 'ms', '>>>>>', team);
return E.right(true);
}
@@ -197,26 +135,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
const isValidTitle = this.validateTeamName(newName);
if (E.isLeft(isValidTitle)) return isValidTitle;
if (this.enableRawSql) {
const startQ = Date.now();
const ut = await this.conn.query(
`UPDATE "Team" SET "name" = '${newName}' WHERE "id" = '${teamID}' RETURNING *`,
);
const endQ = Date.now();
console.log(
'renameTeam >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
ut.rows[0],
);
return E.right(<Team>{
id: ut.rows[0].id,
name: ut.rows[0].name,
});
}
try {
const updatedTeam = await this.prisma.team.update({
where: {
@@ -238,48 +156,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
userUid: string,
newRole: TeamMemberRole,
): Promise<E.Left<string> | E.Right<TeamMember>> {
if (this.enableRawSql) {
const startQ = Date.now();
const oc = await this.conn.query(
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${TeamMemberRole.OWNER}';`,
);
const tm = await this.conn.query(
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
);
if (tm.rows.length === 0) return E.left(TEAM_MEMBER_NOT_FOUND);
const ownerCount = oc.rows[0].count;
if (
tm.rows[0].role === TeamMemberRole.OWNER &&
newRole != TeamMemberRole.OWNER &&
ownerCount === 1
) {
return E.left(TEAM_ONLY_ONE_OWNER);
}
const utm = await this.conn.query(
`UPDATE "teamMember" SET "role" = '${newRole}' WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
);
const endQ = Date.now();
console.log(
'updateTeamMemberRole >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
utm.rows[0],
);
const updatedMember: TeamMember = {
membershipID: utm.rows[0].id,
userUid: utm.rows[0].userUid,
role: TeamMemberRole[utm.rows[0].role],
};
this.pubsub.publish(`team/${teamID}/member_updated`, updatedMember);
return E.right(updatedMember);
}
const startQ = Date.now();
const ownerCount = await this.prisma.teamMember.count({
where: {
teamID,
@@ -316,14 +192,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
role: newRole,
},
});
const endQ = Date.now();
console.log(
'updateTeamMemberRole >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
result,
);
const updatedMember: TeamMember = {
membershipID: result.id,
@@ -340,30 +208,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
teamID: string,
userUid: string,
): Promise<E.Left<string> | E.Right<boolean>> {
if (this.enableRawSql) {
const oc = await this.conn.query(
`SELECT COUNT(*) FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "role" = '${TeamMemberRole.OWNER}';`,
);
const ownerCount = oc.rows[0].count;
console.log('leaveTeam >>>>>>>>>>', oc.rows);
const member = await this.getTeamMember(teamID, userUid);
if (!member) return E.left(TEAM_INVALID_ID_OR_USER);
if (ownerCount === 1 && member.role === TeamMemberRole.OWNER) {
return E.left(TEAM_ONLY_ONE_OWNER);
}
const dtm = await this.conn.query(
`DELETE FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
);
console.log('leaveTeam >>>>>>>>>>', dtm);
this.pubsub.publish(`team/${teamID}/member_removed`, userUid);
return E.right(true);
}
const ownerCount = await this.prisma.teamMember.count({
where: {
teamID,
@@ -404,34 +248,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
const isValidName = this.validateTeamName(name);
if (E.isLeft(isValidName)) return isValidName;
if (this.enableRawSql) {
const startQ = Date.now();
const t = await this.conn.query(
`INSERT INTO "Team" (id, name) VALUES ('${new Date().toISOString()}', '${name}') RETURNING *`,
);
const tm = await this.conn.query(
`INSERT INTO "TeamMember" ("id", "userUid", "teamID", "role") VALUES ('${new Date().toISOString()}', '${creatorUid}' , '${
t.rows[0].id
}', '${TeamMemberRole.OWNER}') RETURNING *`,
);
const endQ = Date.now();
``;
console.log(
'createTeam >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
t.rows[0],
tm.rows[0],
);
return E.right(<Team>{
id: t.rows[0].id,
name: t.rows[0].name,
});
}
const startQ = Date.now();
const team = await this.prisma.team.create({
data: {
name: name,
@@ -443,42 +259,12 @@ export class TeamService implements UserDataHandler, OnModuleInit {
},
},
});
const endQ = Date.now();
console.log('createTeam >>>>>>>>>> ', endQ - startQ, 'ms', '>>>>>', team);
return E.right(team);
}
async getTeamsOfUser(uid: string, cursor: string | null): Promise<Team[]> {
if (this.enableRawSql) {
const startQ = Date.now();
let users;
if (cursor) {
users = await this.conn.query(
`SELECT * FROM "TeamMember" LEFT JOIN "Team" ON "TeamMember"."teamID" = "Team"."id" WHERE "TeamMember"."userUid" = '${uid}' and "TeamMember"."teamID" > '${cursor}' LIMIT 10`,
);
} else {
users = await this.conn.query(
`SELECT * FROM "TeamMember" LEFT JOIN "Team" ON "TeamMember"."teamID" = "Team"."id" WHERE "TeamMember"."userUid" = '${uid}' LIMIT 10`,
);
}
const endQ = Date.now();
console.log(
'getTeamsOfUser >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
users.rows,
);
return users.rows.map((entry) => ({
id: entry.teamID,
name: entry.name,
}));
}
if (!cursor) {
const startQ = Date.now();
const entries = await this.prisma.teamMember.findMany({
take: 10,
where: {
@@ -488,18 +274,9 @@ export class TeamService implements UserDataHandler, OnModuleInit {
team: true,
},
});
const endQ = Date.now();
console.log(
'getTeamsOfUser >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
entries,
);
return entries.map((entry) => entry.team);
} else {
const startQ = Date.now();
const entries = await this.prisma.teamMember.findMany({
take: 10,
skip: 1,
@@ -516,56 +293,17 @@ export class TeamService implements UserDataHandler, OnModuleInit {
team: true,
},
});
const endQ = Date.now();
console.log(
'getTeamsOfUser >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
entries,
);
return entries.map((entry) => entry.team);
}
}
async getTeamWithID(teamID: string): Promise<Team | null> {
if (this.enableRawSql) {
const startQ = Date.now();
const team = await this.conn.query(
`SELECT * FROM "Team" WHERE "id" = '${teamID}'`,
);
const endQ = Date.now();
console.log(
'getTeamWithID >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
team.rows,
);
if (team.rows.length === 0) return null;
return <Team>{
id: team.rows[0].id,
name: team.rows[0].name,
};
}
try {
const startQ = Date.now();
const team = await this.prisma.team.findUnique({
where: {
id: teamID,
},
});
const endQ = Date.now();
console.log(
'getTeamWithID >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
team,
);
return team;
} catch (_e) {
@@ -615,30 +353,7 @@ export class TeamService implements UserDataHandler, OnModuleInit {
teamID: string,
userUid: string,
): Promise<TeamMember | null> {
if (this.enableRawSql) {
const startQ = Date.now();
const member = await this.conn.query(
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "userUid" = '${userUid}'`,
);
const endQ = Date.now();
console.log(
'getTeamMember >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
member.rows,
);
if (member.rows.length === 0) return null;
return <TeamMember>{
membershipID: member.rows[0].id,
userUid: member.rows[0].userUid,
role: TeamMemberRole[member.rows[0].role],
};
}
try {
const startQ = Date.now();
const teamMember = await this.prisma.teamMember.findUnique({
where: {
teamID_userUid: {
@@ -647,14 +362,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
},
},
});
const endQ = Date.now();
console.log(
'getTeamMember >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
teamMember,
);
if (!teamMember) return null;
@@ -726,44 +433,11 @@ export class TeamService implements UserDataHandler, OnModuleInit {
}
async getTeamMembers(teamID: string): Promise<TeamMember[]> {
if (this.enableRawSql) {
const startQ = Date.now();
const members = await this.conn.query(
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}'`,
);
const endQ = Date.now();
console.log(
'getTeamMembers >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
members.rows,
);
return members.rows.map((entry) => {
return {
membershipID: entry.id,
userUid: entry.userUid,
role: TeamMemberRole[entry.role],
};
});
}
const startQ = Date.now();
const dbTeamMembers = await this.prisma.teamMember.findMany({
where: {
teamID,
},
});
const endQ = Date.now();
console.log(
'getTeamMembers >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
dbTeamMembers,
);
const members = dbTeamMembers.map(
(entry) =>
@@ -796,39 +470,8 @@ export class TeamService implements UserDataHandler, OnModuleInit {
teamID: string,
cursor: string | null,
): Promise<TeamMember[]> {
if (this.enableRawSql) {
const startQ = Date.now();
let members;
if (cursor) {
members = await this.conn.query(
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' AND "id" > '${cursor}' LIMIT 10`,
);
}
members = await this.conn.query(
`SELECT * FROM "TeamMember" WHERE "teamID" = '${teamID}' LIMIT 10`,
);
const endQ = Date.now();
console.log(
'getMembersOfTeam >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
members.rows,
);
return members.rows.map(
(entry) =>
<TeamMember>{
membershipID: entry.id,
userUid: entry.userUid,
role: TeamMemberRole[entry.role],
},
);
}
let teamMembers: DbTeamMember[];
const startQ = Date.now();
if (!cursor) {
teamMembers = await this.prisma.teamMember.findMany({
take: 10,
@@ -848,14 +491,6 @@ export class TeamService implements UserDataHandler, OnModuleInit {
},
});
}
const endQ = Date.now();
console.log(
'getMembersOfTeam >>>>>>>>>>',
endQ - startQ,
'ms',
'>>>>>',
teamMembers,
);
const members = teamMembers.map(
(entry) =>

View File

@@ -2,7 +2,20 @@
--font-sans: "Inter Variable", sans-serif;
--font-icon: "Material Symbols Rounded Variable";
--font-mono: "Roboto Mono Variable", monospace;
--font-size-tiny: calc(var(--font-size-body) - 0.062rem);
--font-size-body: 0.75rem;
--font-size-tiny: 0.688rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin dark-theme {
@@ -200,8 +213,8 @@
:root {
@include base-theme;
@include dark-theme;
@include green-theme;
@include dark-editor-theme;
@include green-theme;
}
:root.light {
@@ -257,63 +270,3 @@
:root[data-accent="yellow"] {
@include yellow-theme;
}
@mixin font-small {
--font-size-body: 0.75rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin font-medium {
--font-size-body: 0.875rem;
--line-height-body: 1.25rem;
--upper-primary-sticky-fold: 4.375rem;
--upper-secondary-sticky-fold: 6.688rem;
--upper-tertiary-sticky-fold: 9rem;
--upper-mobile-primary-sticky-fold: 7.125rem;
--upper-mobile-secondary-sticky-fold: 9.438rem;
--upper-mobile-sticky-fold: 11.75rem;
--upper-mobile-tertiary-sticky-fold: 9rem;
--lower-primary-sticky-fold: 3.25rem;
--lower-secondary-sticky-fold: 5.563rem;
--lower-tertiary-sticky-fold: 7.875rem;
--sidebar-primary-sticky-fold: 2.25rem;
}
@mixin font-large {
--font-size-body: 1rem;
--line-height-body: 1.5rem;
--upper-primary-sticky-fold: 4.625rem;
--upper-secondary-sticky-fold: 7.188rem;
--upper-tertiary-sticky-fold: 9.75rem;
--upper-mobile-primary-sticky-fold: 7.625rem;
--upper-mobile-secondary-sticky-fold: 10.188rem;
--upper-mobile-sticky-fold: 12.75rem;
--upper-mobile-tertiary-sticky-fold: 9.75rem;
--lower-primary-sticky-fold: 3.5rem;
--lower-secondary-sticky-fold: 6.063rem;
--lower-tertiary-sticky-fold: 8.625rem;
--sidebar-primary-sticky-fold: 2.5rem;
}
:root[data-font-size="small"] {
@include font-small;
}
:root[data-font-size="medium"] {
@include font-medium;
}
:root[data-font-size="large"] {
@include font-large;
}

View File

@@ -118,22 +118,22 @@
},
"collection": {
"created": "Koleksi dibuat",
"different_parent": "Cannot reorder collection with different parent",
"different_parent": "Tidak dapat mengubah urutan koleksi dengan induk yang berbeda",
"edit": "Mengubah Koleksi",
"invalid_name": "Berikan nama untuk Koleksi",
"invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully",
"invalid_root_move": "Koleksi sudah berada di akar direktori",
"moved": "Berhasil Dipindahkan",
"my_collections": "Koleksi Saya",
"name": "Koleksi Baru Saya",
"name_length_insufficient": "Nama koleksi harus minimal 3 karakter",
"new": "Koleksi baru",
"order_changed": "Collection Order Updated",
"order_changed": "Pembaruan Urutan Koleksi",
"renamed": "Koleksi berganti nama",
"request_in_use": "Permintaan sedang digunakan",
"save_as": "Simpan Sebagai",
"select": "Pilih Koleksi",
"select_location": "Pilih lokasi",
"select_team": "Pilih team",
"select_team": "Pilih tim",
"team_collections": "Koleksi Tim"
},
"confirm": {
@@ -147,7 +147,7 @@
"remove_team": "Apakah Anda yakin ingin menghapus tim ini?",
"remove_telemetry": "Apakah Anda yakin ingin menyisih dari Telemetri?",
"request_change": "Apakah Anda yakin ingin membuang permintaan saat ini, perubahan yang belum disimpan akan hilang.",
"save_unsaved_tab": "Do you want to save changes made in this tab?",
"save_unsaved_tab": "Apakah Anda ingin menyimpan perubahan yang dibuat di tab ini?",
"sync": "Apakah Anda ingin memulihkan ruang kerja Anda dari cloud? Ini akan membuang kemajuan lokal Anda."
},
"count": {
@@ -180,8 +180,8 @@
"profile": "Masuk untuk melihat profil Anda",
"protocols": "Protokol kosong",
"schema": "Hubungkan ke endpoint GraphQL untuk melihat skema",
"shortcodes": "Shortcodes are empty",
"subscription": "Subscriptions are empty",
"shortcodes": "Shortcodes kosong",
"subscription": "Langganan kosong",
"team_name": "Nama team kosong",
"teams": "Kamu bukan di team manapun",
"tests": "Tidak ada tes untuk permintaan ini"
@@ -189,19 +189,19 @@
"environment": {
"add_to_global": "Tambahkan ke Global",
"added": "Tambahan Environment",
"create_new": "Membuat baru environment",
"create_new": "Membuat environment baru",
"created": "Environment dibuat",
"deleted": "Environment dihapus",
"edit": "Sunting Environment",
"invalid_name": "Tolong beri nama untuk environment",
"my_environments": "My Environments",
"nested_overflow": "variabel environment bersarang dibatasi hingga 10 level",
"my_environments": "Environment Saya",
"nested_overflow": "Variabel environment bersarang dibatasi hingga 10 level",
"new": "Environment Baru",
"no_environment": "No environment",
"no_environment_description": "Tidak ada environment yang dipilih. Pilih apa yang harus dilakukan dengan variabel berikut.",
"select": "Pilih environment",
"team_environments": "Team Environments",
"title": "Environments",
"team_environments": "Environment Tim",
"title": "Environment",
"updated": "Environment diperbarui",
"variable_list": "Daftar Variable"
},
@@ -210,8 +210,8 @@
"check_console_details": "Periksa console log untuk detailnya.",
"curl_invalid_format": "cURL tidak diformat dengan benar",
"danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:",
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
"delete_account": "Akun Anda saat ini merupakan pemilik dalam tim-tim ini:",
"delete_account_description": "Anda harus menghapus diri Anda dari tim-tim ini, mentransfer kepemilikan, atau menghapus tim-tim ini sebelum Anda dapat menghapus akun Anda.",
"empty_req_name": "Nama Permintaan Kosong",
"f12_details": "(F12 untuk detailnya)",
"gql_prettify_invalid_query": "Tidak dapat prettify kueri yang tidak valid, menyelesaikan kesalahan sintaksis kueri, dan coba lagi",
@@ -294,7 +294,7 @@
"from_json_description": "Impor dari Hoppscotch berkas koleksi",
"from_my_collections": "Impor dari Koleksi Saya",
"from_my_collections_description": "Impor dari Berkas Koleksi Saya",
"from_openapi": "Import dari OpenAPI",
"from_openapi": "Impor dari OpenAPI",
"from_openapi_description": "Impor dari OpenAPI syarat berkas (YML/JSON)",
"from_postman": "Impor dari Postman",
"from_postman_description": "Impor dari Koleksi Postman",
@@ -316,23 +316,23 @@
"zen_mode": "Zen mode"
},
"modal": {
"close_unsaved_tab": "You have unsaved changes",
"close_unsaved_tab": "Anda memiliki perubahan yang belum disimpan",
"collections": "Koleksi",
"confirm": "Mengonfirmasi",
"edit_request": "Edit Request",
"import_export": "Impor / Ekspor"
},
"mqtt": {
"already_subscribed": "You are already subscribed to this topic.",
"clean_session": "Clean Session",
"clear_input": "Clear input",
"clear_input_on_send": "Clear input on send",
"already_subscribed": "Anda sudah berlangganan topik ini.",
"clean_session": "Sesi Bersih",
"clear_input": "Hapus input",
"clear_input_on_send": "Hapus input saat mengirim",
"client_id": "Client ID",
"color": "Pick a color",
"color": "Pilih warna",
"communication": "Komunikasi",
"connection_config": "Connection Config",
"connection_not_authorized": "This MQTT connection does not use any authentication.",
"invalid_topic": "Please provide a topic for the subscription",
"connection_config": "Konfigurasi Koneksi",
"connection_not_authorized": "Koneksi MQTT ini tidak menggunakan otentikasi",
"invalid_topic": "Harap berikan topik untuk langganan",
"keep_alive": "Keep Alive",
"log": "Log",
"lw_message": "Last-Will Message",
@@ -340,8 +340,8 @@
"lw_retain": "Last-Will Retain",
"lw_topic": "Last-Will Topic",
"message": "Pesan",
"new": "New Subscription",
"not_connected": "Please start a MQTT connection first.",
"new": "Langganan Baru",
"not_connected": "Mulai koneksi MQTT terlebih dahulu",
"publish": "Menerbitkan",
"qos": "QoS",
"ssl": "SSL",
@@ -396,19 +396,19 @@
"text": "Text"
},
"copy_link": "Salin tautan",
"different_collection": "Cannot reorder requests from different collections",
"different_collection": "Tidak dapat mengubah urutan permintaan dari koleksi yang berbeda",
"duplicated": "Request duplicated",
"duration": "Durasi",
"enter_curl": "Masukkan cURL",
"generate_code": "Generate code",
"generated_code": "Generated code",
"generate_code": "Hasilkan kode",
"generated_code": "Hasilkan kode",
"header_list": "Daftar Header",
"invalid_name": "Harap berikan nama untuk request",
"method": "Method",
"moved": "Request moved",
"name": "Request nama",
"new": "Request baru",
"order_changed": "Request Order Updated",
"order_changed": "Urutan Request Diperbarui",
"override": "Membatalkan",
"override_help": "Set <kbd>Content-Type</kbd> in Headers",
"overriden": "Diganti",
@@ -453,7 +453,7 @@
"settings": {
"accent_color": "Accent color",
"account": "Akun",
"account_deleted": "Your account has been deleted",
"account_deleted": "Akun Anda telah dihapus",
"account_description": "Sesuaikan pengaturan akun Anda.",
"account_email_description": "Alamat surel utama Anda.",
"account_name_description": "Ini adalah nama tampilan Anda.",
@@ -609,7 +609,7 @@
"file_imported": "File diimpor",
"finished_in": "Selesai dalam {duration} ms",
"history_deleted": "Riwayat dihapus",
"linewrap": "Wrap lines",
"linewrap": "Bungkus baris",
"loading": "Memuat...",
"message_received": "Pesan: {message} tiba di topik: {topic}",
"mqtt_subscription_failed": "Terjadi masalah saat berlangganan topik: {topic}",
@@ -666,7 +666,7 @@
"email_do_not_match": "Surel tidak cocok dengan detail akun Anda. Hubungi pemilik tim Anda.",
"exit": "Keluar dari Tim",
"exit_disabled": "Hanya pemilik yang tidak dapat keluar dari tim",
"invalid_coll_id": "Invalid collection ID",
"invalid_coll_id": "ID koleksi tidak valid",
"invalid_email_format": "Format surel tidak valid",
"invalid_id": "ID tim tidak valid. Hubungi pemilik tim Anda.",
"invalid_invite_link": "Tautan undangan tidak valid",
@@ -690,7 +690,7 @@
"member_removed": "Pengguna dihapus",
"member_role_updated": "Peran pengguna diperbarui",
"members": "Anggota",
"more_members": "+{count} more",
"more_members": "+{count} lebih",
"name_length_insufficient": "Nama tim harus setidaknya 6 karakter",
"name_updated": "Nama tim diperbarui",
"new": "Tim Baru",
@@ -698,13 +698,13 @@
"new_name": "Tim baru saya",
"no_access": "Anda tidak memiliki akses edit ke collections ini",
"no_invite_found": "Undangan tidak ditemukan. Hubungi pemilik tim Anda.",
"no_request_found": "Request not found.",
"no_request_found": "Request tidak ditemukan.",
"not_found": "Tim tidak ditemukan. Hubungi pemilik tim Anda.",
"not_valid_viewer": "Anda bukan penonton yang valid. Hubungi pemilik tim Anda.",
"parent_coll_move": "Cannot move collection to a child collection",
"parent_coll_move": "Tidak dapat memindahkan koleksi ke dalam koleksi anak",
"pending_invites": "Undangan tertunda",
"permissions": "Izin",
"same_target_destination": "Same target and destination",
"same_target_destination": "Sama tujuan dan destinasi",
"saved": "Tim disimpan",
"select_a_team": "Pilih tim",
"title": "tim",
@@ -712,9 +712,9 @@
"we_sent_invite_link_description": "Minta semua undangan untuk memeriksa kotak masuk mereka. Klik tautan untuk bergabung dengan tim."
},
"team_environment": {
"deleted": "Environment Deleted",
"duplicate": "Environment Duplicated",
"not_found": "Environment not found."
"deleted": "Environment dihapus",
"duplicate": "Environment diduplikasi",
"not_found": "Environment tidak ditemukan."
},
"test": {
"failed": "Tes gagal",
@@ -734,9 +734,9 @@
"url": "URL"
},
"workspace": {
"change": "Change workspace",
"personal": "My Workspace",
"team": "Team Workspace",
"change": "Beralih workspace",
"personal": "Workspace Saya",
"team": "Workspace Tim",
"title": "Workspaces"
}
}

View File

@@ -1,48 +1,50 @@
{
"action": {
"autoscroll": "Autoscroll",
"autoscroll": "Автоскрол",
"cancel": "Отменить",
"choose_file": "Выберите файл",
"clear": "Очистить",
"clear_all": "Очистить все",
"close": "Close",
"clear_history": "Очистить всю историю",
"close": "Закрыть",
"connect": "Подключиться",
"connecting": "Connecting",
"connecting": "Соединение...",
"copy": "Скопировать",
"delete": "Удалить",
"disconnect": "Отключиться",
"dismiss": "Скрыть",
"dont_save": "Don't save",
"dont_save": "Не сохранять",
"download_file": "Скачать файл",
"drag_to_reorder": "Drag to reorder",
"drag_to_reorder": "Перетягивайте для сортировки",
"duplicate": "Дублировать",
"edit": "Редактировать",
"filter": "Filter",
"filter": "Фильтр",
"go_back": "Вернуться",
"go_forward": "Go forward",
"group_by": "Group by",
"go_forward": "Вперёд",
"group_by": "Сгруппировать по",
"label": "Название",
"learn_more": "Узнать больше",
"less": "Less",
"less": "Меньше",
"more": "Больше",
"new": "Создать новый",
"no": "Нет",
"open_workspace": "Open workspace",
"paste": "Paste",
"open_workspace": "Открыть пространство",
"paste": "Вставить",
"prettify": "Форматировать",
"rename": "Переименовать",
"remove": "Удалить",
"restore": "Восстановить",
"save": "Сохранить",
"scroll_to_bottom": "Scroll to bottom",
"scroll_to_top": "Scroll to top",
"scroll_to_bottom": "Вниз",
"scroll_to_top": "Вверх",
"search": "Поиск",
"send": "Отправить",
"start": "Начать",
"starting": "Starting",
"starting": "Запускаю",
"stop": "Стоп",
"to_close": "to close",
"to_navigate": "to navigate",
"to_select": "to select",
"to_close": "что бы закрыть",
"to_navigate": "для навигации",
"to_select": "выборать",
"turn_off": "Выключить",
"turn_on": "Включить",
"undo": "Отменить",
@@ -56,9 +58,9 @@
"chat_with_us": "Связаться с нами",
"contact_us": "Свяжитесь с нами",
"copy": "Копировать",
"copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
"copy_user_id": "Копировать токен пользователя",
"developer_option": "Настройки разработчика",
"developer_option_description": "Инструмент разработчика помогает обслуживить и развивить Hoppscotch",
"discord": "Discord",
"documentation": "Документация",
"github": "GitHub",
@@ -67,11 +69,13 @@
"invite": "Пригласить",
"invite_description": "В Hoppscotch мы разработали простой и интуитивно понятный интерфейс для создания и управления вашими API. Hoppscotch - это инструмент, который помогает создавать, тестировать, документировать и делиться своими API.",
"invite_your_friends": "Пригласить своих друзей",
"social_links": "Социальные сети",
"social_description": "Подписывайся на наши соц. сети и оставайся всегда в курсе последних новостей, обновлений и релизов.",
"join_discord_community": "Присоединяйтесь к нашему сообществу Discord",
"keyboard_shortcuts": "Горячие клавиши",
"name": "Hoppscotch",
"new_version_found": "Найдена новая версия. Перезагрузите для обновления.",
"options": "Options",
"options": "Настройки",
"proxy_privacy_policy": "Политика конфиденциальности прокси",
"reload": "Перезагрузить",
"search": "Поиск",
@@ -79,7 +83,7 @@
"shortcuts": "Ярлыки",
"spotlight": "Прожектор",
"status": "Статус",
"status_description": "Check the status of the website",
"status_description": "Проверить состояние сайта",
"terms_and_privacy": "Условия и конфиденциальность",
"twitter": "Twitter",
"type_a_command_search": "Введите команду или выполните поиск…",
@@ -93,7 +97,7 @@
"continue_with_email": "Продолжить с электронной почтой",
"continue_with_github": "Продолжить с GitHub",
"continue_with_google": "Продолжить с Google",
"continue_with_microsoft": "Continue with Microsoft",
"continue_with_microsoft": "Продолжить с Microsoft",
"email": "Электронное письмо",
"logged_out": "Вышли из",
"login": "Авторизоваться",
@@ -118,19 +122,20 @@
},
"collection": {
"created": "Коллекция создана",
"different_parent": "Cannot reorder collection with different parent",
"different_parent": "Нельзя сортировать коллекцию с разной родительской коллекцией",
"edit": "Редактировать коллекцию",
"invalid_name": "Укажите допустимое название коллекции",
"invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully",
"invalid_root_move": "Коллекция уже в корне",
"moved": "Перемещено успешно",
"my_collections": "Мои коллекции",
"name": "Новая коллекция",
"name_length_insufficient": "Collection name should be at least 3 characters long",
"name_length_insufficient": "Имя коллекции должно иметь 3 или более символов",
"new": "Создать коллекцию",
"order_changed": "Collection Order Updated",
"order_changed": "Порядок коллекции обновлён",
"renamed": "Коллекция переименована",
"request_in_use": "Запрос обрабатывается",
"save_as": "Сохранить как",
"save_to_collection": "Сохранить в коллекцию",
"select": "Выбрать коллекцию",
"select_location": "Выберите местоположение",
"select_team": "Выберите команду",
@@ -146,10 +151,17 @@
"remove_request": "Вы уверены, что хотите навсегда удалить этот запрос?",
"remove_team": "Вы уверены, что хотите удалить эту команду?",
"remove_telemetry": "Вы действительно хотите отказаться от телеметрии?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
"save_unsaved_tab": "Do you want to save changes made in this tab?",
"request_change": "Вы уверены что хотите сбросить текущий запрос, все не сохранённые данные будт утеряны?",
"save_unsaved_tab": "Вы хотите сохранить изменения в этой вкладке?",
"close_unsaved_tab": "Вы уверены что хотите закрыть эту вкладку?",
"close_unsaved_tabs": "ВЫ уверены что хотите закрыть все эти вкладки? Несохранённые данные {count} вкладок будут утеряны.",
"sync": "Вы уверены, что хотите синхронизировать это рабочее пространство?"
},
"context_menu": {
"set_environment_variable": "Назначить как переменную",
"add_parameters": "Добавить в параметры",
"open_request_in_new_tab": "Открыть в новой вкладке"
},
"count": {
"header": "Заголовок {count}",
"message": "Тело {count}",
@@ -180,83 +192,102 @@
"profile": "Войдите, чтобы просмотреть свой профиль",
"protocols": "Протоколы пустые",
"schema": "Подключиться к конечной точке GraphQL",
"shortcodes": "Shortcodes are empty",
"subscription": "Subscriptions are empty",
"shortcodes": "Нет коротких ссылок",
"subscription": "Нет подписок",
"team_name": "Название команды пусто",
"teams": "Команды пустые",
"tests": "Для этого запроса нет тестов"
},
"environment": {
"add_to_global": "Add to Global",
"added": "Environment addition",
"create_new": "Создать новую среду",
"created": "Environment created",
"deleted": "Environment deletion",
"edit": "Редактировать среду",
"invalid_name": "Укажите допустимое имя для среды",
"my_environments": "My Environments",
"nested_overflow": "nested environment variables are limited to 10 levels",
"add_to_global": "Добавить в глобальное окружение",
"added": "Окружение добавлено",
"create_new": "Создать новое окружение",
"created": "Окружение создано",
"deleted": "Окружение удалено",
"duplicated": "Окружение скопировано",
"global": "Глобальное окружение",
"empty_variables": "Нет переменных",
"global_variables": "Глобальные переменные",
"edit": "Редактировать окружение",
"invalid_name": "Укажите допустимое имя для окружения",
"list": "Переменные окружения",
"my_environments": "Мои окружения",
"name": "Название",
"nested_overflow": "максимальный уровень вложения переменных окружения - 10",
"new": "Новая среда",
"no_environment": "Нет окружающей среды",
"no_environment_description": "No environments were selected. Choose what to do with the following variables.",
"no_active_environment": "Нет активных окружений",
"no_environment": "Нет окружения",
"no_environment_description": "Не выбрано окружение, выберите что делать с переменными.",
"quick_peek": "Быстрый просмотр окружения",
"replace_with_variable": "Заменить переменной",
"scope": "Scope",
"select": "Выберите среду",
"team_environments": "Team Environments",
"title": "Среды",
"updated": "Environment updation",
"set": "Выбрать окружение",
"set_as_environment": "Установить как окружение",
"team_environments": "Окружения команды",
"title": "Окружения",
"updated": "Окружение обновлено",
"value": "Значение",
"variable": "Переменная",
"variable_list": "Список переменных"
},
"error": {
"browser_support_sse": "Похоже, в этом браузере нет поддержки событий, отправленных сервером.",
"check_console_details": "Подробности смотрите в журнале консоли.",
"curl_invalid_format": "cURL неправильно отформатирован",
"danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:",
"delete_account_description": "You must either remove yourself, transfer ownership, or delete these teams before you can delete your account.",
"danger_zone": "Опасная зона",
"delete_account": "Вы являетесь владельцем этой команды:",
"delete_account_description": "Прежде чем удалить аккаунт вам необходимо либо назначить владельцом другого пользователя, либо удалить команды в которых вы являетесь владельцем.",
"empty_req_name": "Пустое имя запроса",
"f12_details": "(F12 для подробностей)",
"gql_prettify_invalid_query": "Не удалось определить недопустимый запрос, устранить синтаксические ошибки запроса и повторить попытку.",
"incomplete_config_urls": "Incomplete configuration URLs",
"incorrect_email": "Incorrect email",
"invalid_link": "Invalid link",
"incomplete_config_urls": "Не заполнены URL конфигурации",
"incorrect_email": "Не корректный Email",
"invalid_link": "Не корректная ссылка",
"invalid_link_description": "Ссылка, по которой вы перешли, - недействительна, либо срок ее действия истек.",
"json_parsing_failed": "Invalid JSON",
"json_parsing_failed": "Не корректный JSON",
"json_prettify_invalid_body": "Не удалось определить недопустимое тело, устранить синтаксические ошибки json и повторить попытку.",
"network_error": "Похоже, возникла проблема с соединением. Попробуйте еще раз.",
"network_fail": "Не удалось отправить запрос",
"no_duration": "Без продолжительности",
"no_results_found": "No matches found",
"page_not_found": "This page could not be found",
"no_results_found": "Совпадения не найдены",
"page_not_found": "Эта страница не найдена",
"proxy_error": "Ошибка прокси",
"script_fail": "Не удалось выполнить сценарий предварительного запроса",
"something_went_wrong": "Что-то пошло не так",
"test_script_fail": "Could not execute post-request script"
"test_script_fail": "Не удалось выполнить тестирование запроса"
},
"export": {
"as_json": "Экспорт как JSON",
"create_secret_gist": "Создать секретный Gist",
"gist_created": "Gist создан",
"require_github": "Войдите через GitHub, чтобы создать секретную суть",
"title": "Export"
"title": "Экспорт"
},
"filter": {
"all": "All",
"none": "None",
"starred": "Starred"
"all": "Все",
"none": "Не указано",
"starred": "Отмечено"
},
"folder": {
"created": "Папка создана",
"edit": "Редактировать папку",
"invalid_name": "Укажите имя для папки",
"name_length_insufficient": "Folder name should be at least 3 characters long",
"name_length_insufficient": "Имя папки должно содержать 3 или более символов",
"new": "Новая папка",
"renamed": "Папка переименована"
},
"graphql": {
"mutations": "Мутации",
"schema": "Схема",
"subscriptions": "Подписки"
"subscriptions": "Подписки",
"switch_connection": "Изменить соединение",
"connection_switch_url": "Вы присоединились к GraphQL, URL соединения",
"connection_switch_new_url": "Смена вкладки разорвёт текущее GraphQL соединение. Новый URL соединения будет",
"connection_switch_confirm": "Вы желаете соединиться с последним GraphQL сервером?"
},
"group": {
"time": "Time",
"time": "Время",
"url": "URL"
},
"header": {
@@ -273,11 +304,11 @@
"post_request_tests": "Сценарии тестирования написаны на JavaScript и запускаются после получения ответа.",
"pre_request_script": "Скрипты предварительного запроса написаны на JavaScript и запускаются перед отправкой запроса.",
"script_fail": "Похоже, в скрипте предварительного запроса есть сбой. Проверьте ошибку ниже и исправьте скрипт соответствующим образом.",
"test_script_fail": "There seems to be an error with test script. Please fix the errors and run tests again",
"test_script_fail": "Похоже, что скрипт тестирования содержит ошибку. Пожалуйста исправьте её и попробуйте снова",
"tests": "Напишите тестовый сценарий для автоматизации отладки."
},
"hide": {
"collection": "Collapse Collection Panel",
"collection": "Свернуть панель соединения",
"more": "Скрыть больше",
"preview": "Скрыть предварительный просмотр",
"sidebar": "Скрыть боковую панель"
@@ -287,61 +318,85 @@
"curl": "Импортировать cURL",
"failed": "Ошибка импорта",
"from_gist": "Импорт из Gist",
"from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia",
"from_insomnia_description": "Import from Insomnia collection",
"from_json": "Import from Hoppscotch",
"from_json_description": "Import from Hoppscotch collection file",
"from_gist_description": "Импортировать через Gist URL",
"from_insomnia": "Импортировать с Insomnia",
"from_insomnia_description": "Импортировать из коллекции Insomnia",
"from_json": "Импортировать из Hoppscotch",
"from_json_description": "Импортировать из файла коллекции Hoppscotch",
"from_my_collections": "Импортировать из моих коллекций",
"from_my_collections_description": "Import from My Collections file",
"from_openapi": "Import from OpenAPI",
"from_openapi_description": "Import from OpenAPI specification file (YML/JSON)",
"from_postman": "Import from Postman",
"from_postman_description": "Import from Postman collection",
"from_url": "Import from URL",
"from_my_collections_description": "Импортировать коллекции из моего файла",
"from_openapi": "Импортировать из OpenAPI",
"from_openapi_description": "Импортировать из OpenAPI файла описания API (YML/JSON)",
"from_postman": "Импортировать из Postman",
"from_postman_description": "Импортировать из коллекции Postman",
"from_url": "Импортировать из URL",
"gist_url": "Введите URL-адрес Gist",
"import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported",
"json_description": "Import collections from a Hoppscotch Collections JSON file",
"import_from_url_invalid_fetch": "Не удалить получить данные по этому URL",
"import_from_url_invalid_file_format": "Ошибка при импорте коллекций",
"import_from_url_invalid_type": "Неподдерживаемый тип. Поддерживаемые типы: 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Коллекция импортирована",
"json_description": "Импортировать из коллекции Hoppscotch",
"title": "Импортировать"
},
"inspections": {
"title": "Инспектор",
"description": "Помогает обноружить возможные проблемы",
"environment": {
"add_environment": "Добавить в окружение",
"not_found": "Переменная окружения “{environment}” не найдена."
},
"header": {
"cookie": "Браузерная версия Hoppscotch не может использовать Cookie Header. Пока мы работаем над созданием десктоп версии Hoppscotch, пожалуйста используйте Authorization Header."
},
"response": {
"401_error": "Пожалуйста проверьте ваши параметры авторизации",
"404_error": "Пожалуйста проверьте URL и Метод вашего запроса",
"network_error": "Пожалуйста проверьте соединение",
"cors_error": "Пожалуйста проверьте вашу Cross-Origin Resource Sharing настройку сервера.",
"default_error": "Проверьте ваш запрос."
},
"url": {
"extension_not_installed": "Расширение не установлено",
"extention_not_enabled": "Расширение не включено",
"extention_enable_action": "Включить расширение браузера",
"extension_unknown_origin": "Убедитесь что вы добавили адрес сервера в Hoppscotch расширение."
}
},
"layout": {
"collapse_collection": "Collapse or Expand Collections",
"collapse_sidebar": "Collapse or Expand the sidebar",
"column": "Вертикальное оформление",
"name": "Layout",
"row": "Горизонтальное оформление",
"collapse_collection": "Свернуть или развернуть коллекции",
"collapse_sidebar": "Свернуть или развернуть боковую панель",
"column": "Вертикальная развёртка",
"name": "Развёртка",
"row": "Горизонтальная развертка",
"zen_mode": "Спокойный режим"
},
"modal": {
"close_unsaved_tab": "You have unsaved changes",
"close_unsaved_tab": "У вас есть не сохранённые изменения",
"collections": "Коллекции",
"confirm": "Подтверждать",
"edit_request": "Изменить запрос",
"import_export": "Импорт Экспорт"
},
"mqtt": {
"already_subscribed": "You are already subscribed to this topic.",
"clean_session": "Clean Session",
"clear_input": "Clear input",
"clear_input_on_send": "Clear input on send",
"already_subscribed": "Вы уже подписаны на этот топик",
"clean_session": "Очистить сессию",
"clear_input": "Очистить ввод",
"clear_input_on_send": "Очистить ввод перед отправкой",
"client_id": "Client ID",
"color": "Pick a color",
"color": "Выбрать цвет",
"communication": "Коммуникация",
"connection_config": "Connection Config",
"connection_not_authorized": "This MQTT connection does not use any authentication.",
"invalid_topic": "Please provide a topic for the subscription",
"keep_alive": "Keep Alive",
"connection_config": "Конфигурация соединения",
"connection_not_authorized": "Это соединение MQTT не использует какую-либо авторизацию.",
"invalid_topic": "Пожалуйста выберите topic для подписки",
"keep_alive": "Поддерживать соединение",
"log": "Лог",
"lw_message": "Last-Will Message",
"lw_qos": "Last-Will QoS",
"lw_retain": "Last-Will Retain",
"lw_topic": "Last-Will Topic",
"message": "Сообщение",
"new": "New Subscription",
"not_connected": "Please start a MQTT connection first.",
"new": "Новая подписка",
"not_connected": "Пожалуйста, сначала запустите MQTT соединение.",
"publish": "Публиковать",
"qos": "QoS",
"ssl": "SSL",
@@ -355,7 +410,7 @@
"navigation": {
"doc": "Документы",
"graphql": "GraphQL",
"profile": "Profile",
"profile": "Профиль",
"realtime": "В реальном времени",
"rest": "REST",
"settings": "Настройки"
@@ -363,12 +418,12 @@
"preRequest": {
"javascript_code": "Код JavaScript",
"learn": "Читать документацию",
"script": "Сценарий предварительного запроса",
"snippets": "Фрагменты"
"script": "Предворительный скрипт запроса",
"snippets": "Готовый код"
},
"profile": {
"app_settings": "Настройки приложения",
"default_hopp_displayname": "Unnamed User",
"default_hopp_displayname": "Безымянный",
"editor": "Редактор",
"editor_description": "Редакторы могут добавлять, редактировать, а так же удалять запросы.",
"email_verification_mail": "На вашу электронную почту отправлено письмо для подтверждения. Перейдите по ссылке из письма, чтобы подтвердить свой электронный адрес.",
@@ -391,13 +446,13 @@
"choose_language": "Выберите язык",
"content_type": "Тип содержимого",
"content_type_titles": {
"others": "Others",
"structured": "Structured",
"text": "Text"
"others": "Другие",
"structured": "Структурированный",
"text": "Текст"
},
"copy_link": "Копировать ссылку",
"different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated",
"different_collection": "Нельзя изменять порядок запросов из разных коллекций",
"duplicated": "Запрос скопирован",
"duration": "Продолжительность",
"enter_curl": "Введите cURL",
"generate_code": "Сгенерировать код",
@@ -405,13 +460,13 @@
"header_list": "Список заголовков",
"invalid_name": "Укажите имя для запроса",
"method": "Методика",
"moved": "Request moved",
"moved": "Запрос перемещён",
"name": "Имя запроса",
"new": "New Request",
"order_changed": "Request Order Updated",
"override": "Override",
"override_help": "Set <kbd>Content-Type</kbd> in Headers",
"overriden": "Overridden",
"new": "Новый запрос",
"order_changed": "Порядок запроса изменён",
"override": "Переопределить",
"override_help": "Установить <kbd>Content-Type</kbd> в Заголовках",
"overriden": "Переопределено",
"parameter_list": "Параметры запроса",
"parameters": "Параметры",
"path": "Путь",
@@ -424,17 +479,17 @@
"save_as": "Сохранить как",
"saved": "Запрос сохранен",
"share": "Делиться",
"share_description": "Share Hoppscotch with your friends",
"share_description": "Поделиться Hoppscotch с друзьями",
"title": "Запрос",
"type": "Тип запроса",
"url": "URL",
"variables": "Переменные",
"view_my_links": "View my links"
"view_my_links": "Посмотреть мои ссылки"
},
"response": {
"audio": "Audio",
"audio": "Аудио",
"body": "Тело ответа",
"filter_response_body": "Filter JSON response body (uses JSONPath syntax)",
"filter_response_body": "Отфильтровать ответ в формате JSON (используется синтаксис JSONPath)",
"headers": "Заголовки",
"html": "HTML",
"image": "Изображение",
@@ -446,14 +501,14 @@
"status": "Статус",
"time": "Время",
"title": "Ответ",
"video": "Video",
"video": "Видео",
"waiting_for_connection": "Ожидание соединения",
"xml": "XML"
},
"settings": {
"accent_color": "Основной цвет",
"account": "Счет",
"account_deleted": "Your account has been deleted",
"account_deleted": "Ваш аккаунт был удалён",
"account_description": "Настройте параметры своей учетной записи.",
"account_email_description": "Ваш основной адрес электронной почты.",
"account_name_description": "Это ваше отображаемое имя.",
@@ -462,8 +517,8 @@
"change_font_size": "Изменить размер шрифта",
"choose_language": "Выберите язык",
"dark_mode": "Темный",
"delete_account": "Delete account",
"delete_account_description": "Once you delete your account, all your data will be permanently deleted. This action cannot be undone.",
"delete_account": "Удалить аккаунт",
"delete_account_description": "Удаление аккаунта нельзя отменить",
"expand_navigation": "Раскрыть панель навигации",
"experiments": "Эксперименты",
"experiments_notice": "Это набор экспериментов, над которыми мы работаем, которые могут оказаться полезными, интересными, и тем, и другим, или ни тем, ни другим. Они не окончательные и могут быть нестабильными, поэтому, если произойдет что-то слишком странное, не паникуйте. Просто выключи эту чертову штуку. Шутки в сторону,",
@@ -490,8 +545,8 @@
"proxy_use_toggle": "Используйте промежуточное ПО прокси для отправки запросов",
"read_the": "Прочтите",
"reset_default": "Восстановление значений по умолчанию",
"short_codes": "Short codes",
"short_codes_description": "Short codes which were created by you.",
"short_codes": "Короткие ссылки",
"short_codes_description": "Короткие ссылки, созданные вами",
"sidebar_on_left": "Панель слева",
"sync": "Синхронизировать",
"sync_collections": "Коллекции",
@@ -505,16 +560,16 @@
"theme_description": "Настройте тему своего приложения.",
"use_experimental_url_bar": "Использовать экспериментальную строку URL с выделением среды",
"user": "Пользователь",
"verified_email": "Verified email",
"verify_email": "Подтвердить почту"
"verified_email": "Проверенный Email",
"verify_email": "Подтвердить Email"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"actions": "Действия",
"created_on": "Создано",
"deleted": "Удалёна",
"method": "Метод",
"not_found": "Короткая ссылка не найдена",
"short_code": "Короткая ссылка",
"url": "URL"
},
"shortcut": {
@@ -525,6 +580,10 @@
"show_all": "Горячие клавиши",
"title": "Общий"
},
"others": {
"title": "Другие",
"prettify": "Прекрасные редакторы"
},
"miscellaneous": {
"invite": "Пригласите людей в Hoppscotch",
"title": "Разное"
@@ -545,6 +604,9 @@
"delete_method": "Выберите метод DELETE",
"get_method": "Выберите метод GET",
"head_method": "Выберите метод HEAD",
"rename": "Переименовать запрос",
"import_curl": "Импорт из cURL",
"show_code": "Сгенерировать готовый код",
"method": "Методика",
"next_method": "Выберите следующий метод",
"post_method": "Выберите метод POST",
@@ -553,35 +615,120 @@
"reset_request": "Сбросить запрос",
"save_to_collections": "Сохранить в коллекции",
"send_request": "Послать запрос",
"save_request": "Сохарнить запрос",
"title": "Запрос"
},
"response": {
"copy": "Copy response to clipboard",
"download": "Download response as file",
"title": "Response"
"copy": "Копировать запрос в буфер обмена",
"download": "Скачать запрос как файл",
"title": "Запрос"
},
"theme": {
"black": "Switch theme to black mode",
"dark": "Switch theme to dark mode",
"light": "Switch theme to light mode",
"system": "Switch theme to system mode",
"title": "Theme"
"black": "Черный режим",
"dark": "Тёмный режим",
"light": "Светлый режим",
"system": "Определяется системой",
"title": "Тема"
}
},
"show": {
"code": "Показать код",
"collection": "Expand Collection Panel",
"collection": "Развернуть панель коллекций",
"more": "Показать больше",
"sidebar": "Показать боковую панель"
},
"socketio": {
"communication": "Коммуникация",
"connection_not_authorized": "This SocketIO connection does not use any authentication.",
"connection_not_authorized": "Это SocketIO соединение не использует какую-либо авторизацию.",
"event_name": "Название события",
"events": "События",
"log": "Лог",
"url": "URL"
},
"spotlight": {
"general": {
"help_menu": "Помощь и поддержка",
"chat": "Чат с поддержкой",
"open_docs": "Прочитать документацию",
"open_keybindings": "Горячие клавиши",
"open_github": "Открыть GitHub репозиторий",
"social": "Соц. сети",
"title": "Основное"
},
"miscellaneous": {
"invite": "Пригласите друзей в Hoppscotch",
"title": "Разное"
},
"request": {
"switch_to": "Перейти к",
"select_method": "Выбрать метод",
"save_as_new": "Сохранить как новый запрос",
"tab_parameters": "Параметры",
"tab_body": "Тело",
"tab_headers": "Заголовки",
"tab_authorization": "Авторизация",
"tab_pre_request_script": "Пред-скрипт",
"tab_tests": "Тесты",
"tab_query": "Запрос",
"tab_variables": "Переменные"
},
"graphql": {
"connect": "Соединиться",
"disconnect": "Разъединить"
},
"response": {
"copy": "Копировать ответ",
"download": "Скачать ответ",
"title": "Ответ"
},
"environments": {
"new": "Создать новое окружение",
"new_variable": "Создать новую переменную в окружении",
"edit": "Изменить текущее окружение",
"delete": "Удалить текущее окружение",
"duplicate": "Скопировать текущее окружение",
"edit_global": "Изменить глобальное окружение",
"duplicate_global": "Скопировать глобальное окружение",
"title": "Окружения"
},
"workspace": {
"new": "Создать новую команду",
"edit": "Изменить текущую команду",
"delete": "Удалить текущую команду",
"invite": "Пригласить людей в команду",
"switch_to_personal": "Переключиться на личное пространство",
"title": "Команды"
},
"tab": {
"duplicate": "Скопировать вкладку",
"close_current": "Закрыть вкладку",
"close_others": "Закрыть все кроме этой",
"new_tab": "Открыть в новой вкладке",
"title": "Вкладки"
},
"section": {
"user": "Пользователь",
"theme": "Тема",
"interface": "Интерфейс",
"interceptor": "Перехватчик"
},
"change_language": "Изменить язык",
"settings": {
"theme": {
"black": "Чёрная",
"dark": "Тёмная",
"light": "Светлая",
"system": "Определяется системой"
},
"font": {
"size_sm": "Маленький",
"size_md": "Средний",
"size_lg": "Большой"
},
"change_interceptor": "Изменить перехватчик",
"change_language": "Изменить язык"
}
},
"sse": {
"event_type": "Тип события",
"log": "Лог",
@@ -589,14 +736,14 @@
},
"state": {
"bulk_mode": "Множественное редактирование",
"bulk_mode_placeholder": "Entries are separated by newline\nKeys and values are separated by :\nPrepend # to any row you want to add but keep disabled",
"bulk_mode_placeholder": "Каждый параметр должен начинаться с новой строки\nКлючи и значения разедляются двоеточием\nИспользуйте # для комментария",
"cleared": "Очищено",
"connected": "Связаны",
"connected_to": "Подключено к {name}",
"connecting_to": "Подключение к {name} ...",
"connection_error": "Failed to connect",
"connection_failed": "Connection failed",
"connection_lost": "Connection lost",
"connection_error": "Ошибка подключения",
"connection_failed": "Не удалось установить соединение",
"connection_lost": "Соединение утеряно",
"copied_to_clipboard": "Скопировано в буфер обмена",
"deleted": "Удалено",
"deprecated": "УСТАРЕЛО",
@@ -611,17 +758,17 @@
"history_deleted": "История удалена",
"linewrap": "Обернуть линии",
"loading": "Загрузка...",
"message_received": "Message: {message} arrived on topic: {topic}",
"mqtt_subscription_failed": "Something went wrong while subscribing to topic: {topic}",
"message_received": "Сообщение: {message} получено по топику: {topic}",
"mqtt_subscription_failed": "Что-то пошло не так, при попытке подписаться на топик: {topic}",
"none": "Никто",
"nothing_found": "Ничего не найдено для",
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect",
"subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
"unsubscribed_success": "Successfully unsubscribed from topic: {topic}",
"published_error": "Что-то пошло не так при попытке опубликовать сообщение в топик {topic}: {message}",
"published_message": "Опубликовано сообщение: {message} в топик: {topic}",
"reconnection_error": "Не удалось переподключиться",
"subscribed_failed": "Не удалось подписаться на топик: {topic}",
"subscribed_success": "Успешно подписался на топик: {topic}",
"unsubscribed_failed": "Не удалось отписаться от топика: {topic}",
"unsubscribed_success": "Успешно отписался от топика: {topic}",
"waiting_send_request": "Ожидание отправки запроса"
},
"support": {
@@ -630,7 +777,7 @@
"community": "Задавайте вопросы и помогайте другим",
"documentation": "Узнать больше о Hoppscotch",
"forum": "Задавайте вопросы и получайте ответы",
"github": "Follow us on Github",
"github": "Подпишитесь на нас на Github",
"shortcuts": "Просматривайте приложение быстрее",
"team": "Свяжитесь с командой",
"title": "Служба поддержки",
@@ -641,15 +788,15 @@
"body": "Тело",
"collections": "Коллекции",
"documentation": "Документация",
"environments": "Environments",
"environments": "Окружения",
"headers": "Заголовки",
"history": "История",
"mqtt": "MQTT",
"parameters": "Параметры",
"pre_request_script": "Скрипт предварительного запроса",
"pre_request_script": "Пред-скрипт",
"queries": "Запросы",
"query": "Запрос",
"schema": "Schema",
"schema": "Схема",
"socketio": "Socket.IO",
"sse": "SSE",
"tests": "Тесты",
@@ -666,7 +813,7 @@
"email_do_not_match": "Электронная почта, которой Вы воспользовались не соответсвует указанной в данных Вашей учетной записи.",
"exit": "Выйти из команды",
"exit_disabled": "Только владелец не может выйти из команды",
"invalid_coll_id": "Invalid collection ID",
"invalid_coll_id": "Не верный идентификатор коллекции",
"invalid_email_format": "Формат электронной почты недействителен",
"invalid_id": "Некорректный ID команды. Свяжитесь с руководителем команды.",
"invalid_invite_link": "Ссылка недействительна",
@@ -690,7 +837,7 @@
"member_removed": "Пользователь удален",
"member_role_updated": "Роли пользователей обновлены",
"members": "Участники",
"more_members": "+{count} more",
"more_members": "+{count}",
"name_length_insufficient": "Название команды должно быть не менее 6 символов.",
"name_updated": "Название команды обновлено",
"new": "Новая команда",
@@ -698,13 +845,13 @@
"new_name": "Моя новая команда",
"no_access": "У вас нет прав на редактирование этих коллекций",
"no_invite_found": "Такое приглашение мы не смогли найти. Свяжитесь с руководителем команды.",
"no_request_found": "Request not found.",
"not_found": "Team not found. Contact your team owner.",
"no_request_found": "Запрос не найден",
"not_found": "Команда не найдена, свяжитесь с владельцем команды",
"not_valid_viewer": "У Вас нет прав просматривать это. Свяжитесь с руководителем команды.",
"parent_coll_move": "Cannot move collection to a child collection",
"parent_coll_move": "Не удалось переместить коллекцию в дочернюю",
"pending_invites": "Ожидающие приглашения",
"permissions": "Разрешения",
"same_target_destination": "Same target and destination",
"same_target_destination": "Таже цель и конечная точка",
"saved": "Команда сохранена",
"select_a_team": "Выбрать команду",
"title": "Команды",
@@ -712,9 +859,9 @@
"we_sent_invite_link_description": "Попросите тех, кого Вы пригласили, проверить их почтовые ящики. Им нужно перейди по ссылке, чтобы подтвердить вступление в эту команду."
},
"team_environment": {
"deleted": "Environment Deleted",
"duplicate": "Environment Duplicated",
"not_found": "Environment not found."
"deleted": "Окружение удалено",
"duplicate": "Окружение скопировано",
"not_found": "Окружение не найдено"
},
"test": {
"failed": "Тест не пройден",
@@ -734,9 +881,9 @@
"url": "URL"
},
"workspace": {
"change": "Change workspace",
"personal": "My Workspace",
"team": "Team Workspace",
"title": "Workspaces"
"change": "Изменить пространство",
"personal": "Моё пространство",
"team": "Пространство команды",
"title": "Рабочие пространства"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,18 +10,6 @@
:class="{ '-rotate-180': !EXPAND_NAVIGATION }"
@click="EXPAND_NAVIGATION = !EXPAND_NAVIGATION"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="`${ZEN_MODE ? t('action.turn_off') : t('action.turn_on')} ${t(
'layout.zen_mode'
)}`"
:icon="ZEN_MODE ? IconMinimize : IconMaximize"
:class="{
'!text-accent !focus-visible:text-accentDark !hover:text-accentDark':
ZEN_MODE,
}"
@click="ZEN_MODE = !ZEN_MODE"
/>
<tippy interactive trigger="click" theme="popover">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
@@ -211,11 +199,9 @@
</template>
<script setup lang="ts">
import { ref, watch } from "vue"
import { ref } from "vue"
import { version } from "~/../package.json"
import IconSidebar from "~icons/lucide/sidebar"
import IconMinimize from "~icons/lucide/minimize"
import IconMaximize from "~icons/lucide/maximize"
import IconZap from "~icons/lucide/zap"
import IconShare2 from "~icons/lucide/share-2"
import IconColumns from "~icons/lucide/columns"
@@ -241,7 +227,6 @@ const showDeveloperOptions = ref(false)
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
const SIDEBAR = useSetting("SIDEBAR")
const ZEN_MODE = useSetting("ZEN_MODE")
const COLUMN_LAYOUT = useSetting("COLUMN_LAYOUT")
const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
@@ -252,13 +237,6 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser()
)
watch(
() => ZEN_MODE.value,
() => {
EXPAND_NAVIGATION.value = !ZEN_MODE.value
}
)
const nativeShare = () => {
if (navigator.share) {
navigator

View File

@@ -67,7 +67,6 @@
</template>
<script setup lang="ts">
import { watch } from "vue"
import IconSidebar from "~icons/lucide/sidebar"
import IconSidebarOpen from "~icons/lucide/sidebar-open"
import IconChevronRight from "~icons/lucide/chevron-right"
@@ -77,17 +76,9 @@ import { platform } from "~/platform"
const t = useI18n()
const ZEN_MODE = useSetting("ZEN_MODE")
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
const SIDEBAR = useSetting("SIDEBAR")
watch(
() => ZEN_MODE.value,
() => {
EXPAND_NAVIGATION.value = !ZEN_MODE.value
}
)
defineProps<{
show: boolean
}>()

View File

@@ -119,5 +119,8 @@ watch(
&.active {
@apply after:bg-accentLight;
}
scroll-padding: 4rem !important;
scroll-margin: 4rem !important;
}
</style>

View File

@@ -40,7 +40,7 @@
:key="`result-${result.id}`"
:entry="result"
:active="isEqual(selectedEntry, [sectionIndex, entryIndex])"
@mouseover="selectedEntry = [sectionIndex, entryIndex]"
@mouseover="onMouseOver($event, sectionIndex, entryIndex)"
@action="runAction(sectionID, result)"
/>
</div>
@@ -178,6 +178,24 @@ function runAction(searcherID: string, result: SpotlightSearcherResult) {
emit("hide-modal")
}
let lastMousePosition: { x: number; y: number }
const onMouseOver = (
e: MouseEvent,
sectionIndex: number,
entryIndex: number
) => {
const mousePosition = {
x: e.clientX,
y: e.clientY,
}
// if the position is same, do nothing
if (isEqual(lastMousePosition, mousePosition)) return
selectedEntry.value = [sectionIndex, entryIndex]
lastMousePosition = mousePosition
}
function newUseArrowKeysForNavigation() {
const selectedEntry = ref<[number, number]>([0, 0]) // [sectionIndex, entryIndex]

View File

@@ -9,7 +9,7 @@
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
to="https://docs.hoppscotch.io/documentation/getting-started/rest/using-parameters"
to="https://docs.hoppscotch.io/documentation/features/rest-api-testing"
blank
:title="t('app.wiki')"
:icon="IconHelpCircle"

View File

@@ -17,7 +17,7 @@
<span
v-tippy="{ theme: 'tooltip' }"
:title="relativeTime"
class="mx-auto truncate ts-font text-secondaryLight hover:text-secondary hover:text-center"
class="mx-auto truncate text-tiny text-secondaryLight hover:text-secondary hover:text-center"
>
{{ shortDateTime(entry.ts) }}
</span>
@@ -406,8 +406,4 @@ const icon = computed(() => markRaw(ICONS[props.entry.source].icon))
@apply transition;
@apply hover: text-secondary;
}
.ts-font {
font-size: 0.6rem;
}
</style>

View File

@@ -311,35 +311,36 @@ const envVars = computed(() =>
const envTooltipPlugin = new HoppReactiveEnvPlugin(envVars, view)
const initView = (el: any) => {
function handleTextSelection() {
const selection = view.value?.state.selection.main
if (selection) {
const from = selection.from
const to = selection.to
const text = view.value?.state.doc.sliceString(from, to)
const { top, left } = view.value?.coordsAtPos(from)
if (text) {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text,
})
showSuggestionPopover.value = false
} else {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text: null,
})
}
function handleTextSelection() {
const selection = view.value?.state.selection.main
if (selection) {
const from = selection.from
const to = selection.to
if (from === to) return
const text = view.value?.state.doc.sliceString(from, to)
const { top, left } = view.value?.coordsAtPos(from)
if (text) {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text,
})
showSuggestionPopover.value = false
} else {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text: null,
})
}
}
}
const initView = (el: any) => {
// Debounce to prevent double click from selecting the word
const debounceFn = useDebounceFn(() => {
handleTextSelection()
@@ -381,6 +382,11 @@ const initView = (el: any) => {
drop(ev) {
ev.preventDefault()
},
scroll(event) {
if (event.target) {
handleTextSelection()
}
},
}),
ViewPlugin.fromClass(
class {

View File

@@ -1,71 +0,0 @@
<template>
<span class="inline-flex">
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions.focus()"
>
<span class="select-wrapper">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('settings.change_font_size')"
class="pr-8"
:icon="IconType"
outline
:label="`${getFontSizeName(
fontSizes.find((size) => size === active)
)}`"
/>
</span>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
v-for="(size, index) in fontSizes"
:key="`size-${index}`"
:label="`${getFontSizeName(size)}`"
:icon="size === active ? IconCircleDot : IconCircle"
:active="size === active"
@click="
() => {
setActiveFont(size)
hide()
}
"
/>
</div>
</template>
</tippy>
</span>
</template>
<script setup lang="ts">
import IconCircleDot from "~icons/lucide/circle-dot"
import IconCircle from "~icons/lucide/circle"
import IconType from "~icons/lucide/type"
import { HoppFontSizes, HoppFontSize, applySetting } from "~/newstore/settings"
import { useSetting } from "@composables/settings"
import { useI18n } from "@composables/i18n"
import { ref } from "vue"
const t = useI18n()
const fontSizes = HoppFontSizes
const active = useSetting("FONT_SIZE")
const getFontSizeName = (size: HoppFontSize) => {
return t(`settings.font_size_${size}`)
}
const setActiveFont = (size: HoppFontSize) => {
applySetting("FONT_SIZE", size)
}
// Template refs
const tippyActions = ref<any | null>(null)
</script>

View File

@@ -216,6 +216,33 @@ export function useCodemirror(
? new HoppEnvironmentPlugin(subscribeToStream, view)
: null
function handleTextSelection() {
const selection = view.value?.state.selection.main
if (selection) {
const from = selection.from
const to = selection.to
const text = view.value?.state.doc.sliceString(from, to)
const { top, left } = view.value?.coordsAtPos(from)
if (text) {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text,
})
} else {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text: null,
})
}
}
}
const initView = (el: any) => {
if (el) platform.ui?.onCodemirrorInstanceMount?.(el)
@@ -226,33 +253,6 @@ export function useCodemirror(
ViewPlugin.fromClass(
class {
update(update: ViewUpdate) {
function handleTextSelection() {
const selection = view.value?.state.selection.main
if (selection) {
const from = selection.from
const to = selection.to
const text = view.value?.state.doc.sliceString(from, to)
const { top, left } = view.value?.coordsAtPos(from)
if (text) {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text,
})
} else {
invokeAction("contextmenu.open", {
position: {
top,
left,
},
text: null,
})
}
}
}
// Debounce to prevent double click from selecting the word
const debounceFn = useDebounceFn(() => {
handleTextSelection()
@@ -296,6 +296,13 @@ export function useCodemirror(
}
}
),
EditorView.domEventHandlers({
scroll(event) {
if (event.target) {
handleTextSelection()
}
},
}),
EditorView.updateListener.of((update) => {
if (options.extendedEditorConfig.readOnly) {
update.view.contentDOM.inputMode = "none"

View File

@@ -1,7 +1,7 @@
<template>
<div class="flex w-screen h-screen">
<Splitpanes class="no-splitter" :dbl-click-splitter="false" horizontal>
<Pane v-if="!zenMode" style="height: auto">
<Pane style="height: auto">
<AppHeader />
</Pane>
<Pane :class="spacerClass" class="flex flex-1 !overflow-auto md:mb-0">
@@ -79,9 +79,7 @@ const router = useRouter()
const showSearch = ref(false)
const showSupport = ref(false)
const fontSize = useSetting("FONT_SIZE")
const expandNavigation = useSetting("EXPAND_NAVIGATION")
const zenMode = useSetting("ZEN_MODE")
const rightSidebar = useSetting("SIDEBAR")
const columnLayout = useSetting("COLUMN_LAYOUT")
@@ -133,24 +131,9 @@ watch(mdAndLarger, () => {
columnLayout.value = true
}
})
const spacerClass = computed(() => {
if (fontSize.value === "small" && expandNavigation.value)
return "spacer-small"
if (fontSize.value === "medium" && expandNavigation.value)
return "spacer-medium"
if (fontSize.value === "large" && expandNavigation.value)
return "spacer-large"
if (
(fontSize.value === "small" ||
fontSize.value === "medium" ||
fontSize.value === "large") &&
!expandNavigation.value
)
return "spacer-expand"
return ""
})
const spacerClass = computed(() =>
expandNavigation.value ? "spacer-small" : "spacer-expand"
)
defineActionHandler("modals.search.toggle", () => {
showSearch.value = !showSearch.value

View File

@@ -68,23 +68,9 @@ const applyAccentColor = (_app: App) => {
)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const applyFontSize = (_app: App) => {
const [pref] = useSettingStatic("FONT_SIZE")
watch(
pref,
(newPref) => {
document.documentElement.setAttribute("data-font-size", newPref)
},
{ immediate: true }
)
}
export default <HoppModule>{
onVueAppInit(app) {
applyColorMode(app)
applyAccentColor(app)
applyFontSize(app)
},
}

View File

@@ -23,10 +23,6 @@ export const HoppAccentColors = [
export type HoppAccentColor = (typeof HoppAccentColors)[number]
export const HoppFontSizes = ["small", "medium", "large"] as const
export type HoppFontSize = (typeof HoppFontSizes)[number]
export type SettingsDef = {
syncCollections: boolean
syncHistory: boolean
@@ -49,8 +45,6 @@ export type SettingsDef = {
EXPAND_NAVIGATION: boolean
SIDEBAR: boolean
SIDEBAR_ON_LEFT: boolean
ZEN_MODE: boolean
FONT_SIZE: HoppFontSize
COLUMN_LAYOUT: boolean
}
@@ -76,8 +70,6 @@ export const getDefaultSettings = (): SettingsDef => ({
EXPAND_NAVIGATION: true,
SIDEBAR: true,
SIDEBAR_ON_LEFT: true,
ZEN_MODE: false,
FONT_SIZE: "small",
COLUMN_LAYOUT: true,
})

View File

@@ -141,6 +141,7 @@ import { InspectionService } from "~/services/inspection"
import { HeaderInspectorService } from "~/services/inspection/inspectors/header.inspector"
import { EnvironmentInspectorService } from "~/services/inspection/inspectors/environment.inspector"
import { ResponseInspectorService } from "~/services/inspection/inspectors/response.inspector"
import { cloneDeep } from "lodash-es"
const savingRequest = ref(false)
const confirmingCloseForTabID = ref<string | null>(null)
@@ -243,7 +244,7 @@ const duplicateTab = (tabID: string) => {
const tab = getTabRef(tabID)
if (tab.value) {
const newTab = createNewTab({
request: tab.value.document.request,
request: cloneDeep(tab.value.document.request),
isDirty: true,
})
currentTabID.value = newTab.id

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div class="container space-y-8 divide-y divide-dividerLight">
<div class="container divide-y divide-dividerLight">
<div class="md:grid md:gap-4 md:grid-cols-3">
<div class="p-8 md:col-span-1">
<h3 class="heading">
@@ -36,14 +36,6 @@
<SmartAccentModePicker />
</div>
</section>
<section>
<h4 class="font-semibold text-secondaryDark">
{{ t("settings.font_size") }}
</h4>
<div class="mt-4">
<SmartFontSizePicker />
</div>
</section>
<section>
<h4 class="font-semibold text-secondaryDark">
{{ t("settings.language") }}
@@ -90,14 +82,6 @@
{{ t("settings.sidebar_on_left") }}
</HoppSmartToggle>
</div>
<div class="flex items-center">
<HoppSmartToggle
:on="ZEN_MODE"
@change="toggleSetting('ZEN_MODE')"
>
{{ t("layout.zen_mode") }}
</HoppSmartToggle>
</div>
</div>
</section>
</div>
@@ -178,7 +162,6 @@ const PROXY_URL = useSetting("PROXY_URL")
const TELEMETRY_ENABLED = useSetting("TELEMETRY_ENABLED")
const EXPAND_NAVIGATION = useSetting("EXPAND_NAVIGATION")
const SIDEBAR_ON_LEFT = useSetting("SIDEBAR_ON_LEFT")
const ZEN_MODE = useSetting("ZEN_MODE")
const confirmRemove = ref(false)
@@ -186,10 +169,6 @@ const proxySettings = computed(() => ({
url: PROXY_URL.value,
}))
watch(ZEN_MODE, (mode) => {
applySetting("EXPAND_NAVIGATION", !mode)
})
watch(
proxySettings,
({ url }) => {

View File

@@ -72,7 +72,7 @@ export const twitter: HoppSupportOptionsMenuItem = {
icon: IconTwitter,
action: {
type: "link",
href: "https://hoppscotch.io/discord",
href: "https://hoppscotch.io/twitter",
},
}
@@ -84,17 +84,7 @@ export const invite: HoppSupportOptionsMenuItem = {
action: {
type: "custom",
do() {
if (navigator.share) {
navigator
.share({
title: "Hoppscotch",
text: "Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
url: "https://hoppscotch.io",
})
.catch(console.error)
} else {
// fallback
}
invokeAction("modals.share.toggle")
},
},
}

View File

@@ -14,7 +14,6 @@ import IconGlobe from "~icons/lucide/globe"
import IconMonitor from "~icons/lucide/monitor"
import IconMoon from "~icons/lucide/moon"
import IconSun from "~icons/lucide/sun"
import IconCircle from "~icons/lucide/circle"
import IconCheckCircle from "~icons/lucide/check-circle"
type Doc = {
@@ -34,7 +33,6 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
private t = getI18n()
private activeFontSize = useSetting("FONT_SIZE")
private activeTheme = useSetting("BG_COLOR")
public readonly searcherID = "settings"
@@ -91,60 +89,7 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
: markRaw(IconMoon)
),
},
font_size_sm: {
text: [
this.t("settings.font_size"),
this.t("spotlight.settings.font.size_sm"),
],
onClick: () => {
console.log("clicked")
},
alternates: [
"font size",
"change font size",
"change font",
"increase font",
],
icon: computed(() =>
this.activeFontSize.value === "small"
? markRaw(IconCheckCircle)
: markRaw(IconCircle)
),
},
font_size_md: {
text: [
this.t("settings.font_size"),
this.t("spotlight.settings.font.size_md"),
],
alternates: [
"font size",
"change font size",
"change font",
"increase font",
],
icon: computed(() =>
this.activeFontSize.value === "medium"
? markRaw(IconCheckCircle)
: markRaw(IconCircle)
),
},
font_size_lg: {
text: [
this.t("settings.font_size"),
this.t("spotlight.settings.font.size_lg"),
],
alternates: [
"font size",
"change font size",
"change font",
"increase font",
],
icon: computed(() =>
this.activeFontSize.value === "large"
? markRaw(IconCheckCircle)
: markRaw(IconCircle)
),
},
change_lang: {
text: [
this.t("spotlight.section.interface"),
@@ -202,17 +147,6 @@ export class SettingsSpotlightSearcherService extends StaticSpotlightSearcherSer
case "theme_black":
invokeAction("settings.theme.black")
break
// font size actions
case "font_size_sm":
this.activeFontSize.value = "small"
break
case "font_size_md":
this.activeFontSize.value = "medium"
break
case "font_size_lg":
this.activeFontSize.value = "large"
break
}
}
}

View File

@@ -1,8 +1,21 @@
@mixin base-theme {
--font-sans: "Inter Variable", sans-serif;
--font-icon: "Material Symbols Rounded Variable";
--font-mono: "Roboto Mono Variable", monospace;
--font-size-tiny: calc(var(--font-size-body) - 0.062rem);
--font-sans: 'Inter Variable', sans-serif;
--font-icon: 'Material Symbols Rounded Variable';
--font-mono: 'Roboto Mono Variable', monospace;
--font-size-body: 0.75rem;
--font-size-tiny: 0.688rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin dark-theme {
@@ -200,8 +213,8 @@
:root {
@include base-theme;
@include dark-theme;
@include green-theme;
@include dark-editor-theme;
@include green-theme;
}
:root.light {
@@ -257,63 +270,3 @@
:root[data-accent='yellow'] {
@include yellow-theme;
}
@mixin font-small {
--font-size-body: 0.75rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin font-medium {
--font-size-body: 0.875rem;
--line-height-body: 1.25rem;
--upper-primary-sticky-fold: 4.375rem;
--upper-secondary-sticky-fold: 6.688rem;
--upper-tertiary-sticky-fold: 9rem;
--upper-mobile-primary-sticky-fold: 7.125rem;
--upper-mobile-secondary-sticky-fold: 9.438rem;
--upper-mobile-sticky-fold: 11.75rem;
--upper-mobile-tertiary-sticky-fold: 9rem;
--lower-primary-sticky-fold: 3.25rem;
--lower-secondary-sticky-fold: 5.563rem;
--lower-tertiary-sticky-fold: 7.875rem;
--sidebar-primary-sticky-fold: 2.25rem;
}
@mixin font-large {
--font-size-body: 1rem;
--line-height-body: 1.5rem;
--upper-primary-sticky-fold: 4.625rem;
--upper-secondary-sticky-fold: 7.188rem;
--upper-tertiary-sticky-fold: 9.75rem;
--upper-mobile-primary-sticky-fold: 7.625rem;
--upper-mobile-secondary-sticky-fold: 10.188rem;
--upper-mobile-sticky-fold: 12.75rem;
--upper-mobile-tertiary-sticky-fold: 9.75rem;
--lower-primary-sticky-fold: 3.5rem;
--lower-secondary-sticky-fold: 6.063rem;
--lower-tertiary-sticky-fold: 8.625rem;
--sidebar-primary-sticky-fold: 2.5rem;
}
:root[data-font-size='small'] {
@include font-small;
}
:root[data-font-size='medium'] {
@include font-medium;
}
:root[data-font-size='large'] {
@include font-large;
}

View File

@@ -1,20 +1,17 @@
<!DOCTYPE html>
<html lang="en" data-font-size="medium">
<head>
<script>
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"')
</script>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hoppscotch Admin</title>
<meta property="og:image" content="/cover.jpg" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<html lang="en">
<head>
<script>
globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');
</script>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hoppscotch Admin</title>
<meta property="og:image" content="/cover.jpg" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -2,7 +2,20 @@
--font-sans: "Inter Variable", sans-serif;
--font-icon: "Material Symbols Rounded Variable";
--font-mono: "Roboto Mono Variable", monospace;
--font-size-tiny: calc(var(--font-size-body) - 0.062rem);
--font-size-body: 0.75rem;
--font-size-tiny: 0.688rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin dark-theme {
@@ -200,8 +213,8 @@
:root {
@include base-theme;
@include dark-theme;
@include green-theme;
@include dark-editor-theme;
@include green-theme;
}
:root.light {
@@ -257,63 +270,3 @@
:root[data-accent="yellow"] {
@include yellow-theme;
}
@mixin font-small {
--font-size-body: 0.75rem;
--line-height-body: 1rem;
--upper-primary-sticky-fold: 4.125rem;
--upper-secondary-sticky-fold: 6.188rem;
--upper-tertiary-sticky-fold: 8.25rem;
--upper-mobile-primary-sticky-fold: 6.625rem;
--upper-mobile-secondary-sticky-fold: 8.688rem;
--upper-mobile-sticky-fold: 10.75rem;
--upper-mobile-tertiary-sticky-fold: 8.25rem;
--lower-primary-sticky-fold: 3rem;
--lower-secondary-sticky-fold: 5.063rem;
--lower-tertiary-sticky-fold: 7.125rem;
--sidebar-primary-sticky-fold: 2rem;
}
@mixin font-medium {
--font-size-body: 0.875rem;
--line-height-body: 1.25rem;
--upper-primary-sticky-fold: 4.375rem;
--upper-secondary-sticky-fold: 6.688rem;
--upper-tertiary-sticky-fold: 9rem;
--upper-mobile-primary-sticky-fold: 7.125rem;
--upper-mobile-secondary-sticky-fold: 9.438rem;
--upper-mobile-sticky-fold: 11.75rem;
--upper-mobile-tertiary-sticky-fold: 9rem;
--lower-primary-sticky-fold: 3.25rem;
--lower-secondary-sticky-fold: 5.563rem;
--lower-tertiary-sticky-fold: 7.875rem;
--sidebar-primary-sticky-fold: 2.25rem;
}
@mixin font-large {
--font-size-body: 1rem;
--line-height-body: 1.5rem;
--upper-primary-sticky-fold: 4.625rem;
--upper-secondary-sticky-fold: 7.188rem;
--upper-tertiary-sticky-fold: 9.75rem;
--upper-mobile-primary-sticky-fold: 7.625rem;
--upper-mobile-secondary-sticky-fold: 10.188rem;
--upper-mobile-sticky-fold: 12.75rem;
--upper-mobile-tertiary-sticky-fold: 9.75rem;
--lower-primary-sticky-fold: 3.5rem;
--lower-secondary-sticky-fold: 6.063rem;
--lower-tertiary-sticky-fold: 8.625rem;
--sidebar-primary-sticky-fold: 2.5rem;
}
:root[data-font-size="small"] {
@include font-small;
}
:root[data-font-size="medium"] {
@include font-medium;
}
:root[data-font-size="large"] {
@include font-large;
}

167
pnpm-lock.yaml generated
View File

@@ -184,9 +184,6 @@ importers:
passport-microsoft:
specifier: ^1.0.0
version: 1.0.0
pg:
specifier: ^8.11.3
version: 8.11.3
prisma:
specifier: ^4.16.2
version: 4.16.2
@@ -1342,7 +1339,7 @@ importers:
version: 0.13.1(vite@3.2.4)(workbox-build@6.6.0)(workbox-window@6.6.0)
vite-plugin-vue-layouts:
specifier: ^0.7.0
version: 0.7.0(vite@3.2.4)(vue-router@4.2.4)(vue@3.2.45)
version: 0.7.0(vite@3.2.4)(vue-router@4.1.0)(vue@3.2.45)
vite-plugin-windicss:
specifier: ^1.8.8
version: 1.8.8(vite@3.2.4)
@@ -6065,8 +6062,8 @@ packages:
vue-i18n:
optional: true
dependencies:
'@intlify/message-compiler': 9.3.0
'@intlify/shared': 9.3.0
'@intlify/message-compiler': 9.3.0-beta.26
'@intlify/shared': 9.3.0-beta.26
jsonc-eslint-parser: 1.4.1
source-map: 0.6.1
vue-i18n: 9.2.2(vue@3.3.4)
@@ -6119,14 +6116,6 @@ packages:
'@intlify/shared': 9.2.2
source-map: 0.6.1
/@intlify/message-compiler@9.3.0:
resolution: {integrity: sha512-D8tSJEhTCSFcCzkThjE4Sbk1tIdvzkYa1FaVIzUtZ8hKPATvokNrOiDw1i/h671m8A80l9Ywq594i/LPTB6EJA==}
engines: {node: '>= 16'}
dependencies:
'@intlify/shared': 9.3.0
source-map-js: 1.0.2
dev: true
/@intlify/message-compiler@9.3.0-beta.20:
resolution: {integrity: sha512-hwqQXyTnDzAVZ300SU31jO0+3OJbpOdfVU6iBkrmNpS7t2HRnVACo0EwcEXzJa++4EVDreqz5OeqJbt+PeSGGA==}
engines: {node: '>= 16'}
@@ -6135,17 +6124,25 @@ packages:
source-map-js: 1.0.2
dev: true
/@intlify/message-compiler@9.3.0-beta.26:
resolution: {integrity: sha512-qsfU6Lca7mI80ts1vgy+pfNvGm2gHy0nERpT/K1GYgnECmsKwud0e8SG1PPxKPEHKa5Mdngzs4pS7X1wH0SCGA==}
engines: {node: '>= 16'}
dependencies:
'@intlify/shared': 9.3.0-beta.26
source-map-js: 1.0.2
dev: true
/@intlify/shared@9.2.2:
resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
engines: {node: '>= 14'}
/@intlify/shared@9.3.0:
resolution: {integrity: sha512-MMGRz6zWxtz7rHtxIIdnyb8SYOIaaseN1IvUhAEs9tOW4u77RD4DFp4qgPXesp2Gxo/5QitH9kwSs0jnxGUNEw==}
/@intlify/shared@9.3.0-beta.20:
resolution: {integrity: sha512-RucSPqh8O9FFxlYUysQTerSw0b9HIRpyoN1Zjogpm0qLiHK+lBNSa5sh1nCJ4wSsNcjphzgpLQCyR60GZlRV8g==}
engines: {node: '>= 16'}
dev: true
/@intlify/shared@9.3.0-beta.20:
resolution: {integrity: sha512-RucSPqh8O9FFxlYUysQTerSw0b9HIRpyoN1Zjogpm0qLiHK+lBNSa5sh1nCJ4wSsNcjphzgpLQCyR60GZlRV8g==}
/@intlify/shared@9.3.0-beta.26:
resolution: {integrity: sha512-RpCtfSYIg4tSskrazTr5+WCHyw6qpgwdIxC+x3nCnrPGxyk+en9FoSbadVfx/w7uDTdyhKslEw4d2+qhNc0s4Q==}
engines: {node: '>= 16'}
dev: true
@@ -6165,7 +6162,7 @@ packages:
optional: true
dependencies:
'@intlify/bundle-utils': 7.0.0
'@intlify/shared': 9.3.0
'@intlify/shared': 9.3.0-beta.26
'@rollup/pluginutils': 4.2.1
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.2.11
@@ -6192,7 +6189,7 @@ packages:
optional: true
dependencies:
'@intlify/bundle-utils': 3.4.0(vue-i18n@9.2.2)
'@intlify/shared': 9.3.0
'@intlify/shared': 9.3.0-beta.26
'@rollup/pluginutils': 4.2.1
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
@@ -6220,7 +6217,7 @@ packages:
optional: true
dependencies:
'@intlify/bundle-utils': 3.4.0(vue-i18n@9.2.2)
'@intlify/shared': 9.3.0
'@intlify/shared': 9.3.0-beta.26
'@rollup/pluginutils': 4.2.1
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
@@ -6247,7 +6244,7 @@ packages:
optional: true
dependencies:
'@intlify/bundle-utils': 3.4.0(vue-i18n@9.2.2)
'@intlify/shared': 9.3.0
'@intlify/shared': 9.3.0-beta.26
'@rollup/pluginutils': 4.2.1
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
@@ -11141,11 +11138,6 @@ packages:
/buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
/buffer-writer@2.0.0:
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
engines: {node: '>=4'}
dev: false
/buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
dependencies:
@@ -18945,10 +18937,6 @@ packages:
netmask: 2.0.2
dev: false
/packet-reader@1.0.0:
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
dev: false
/paho-mqtt@1.1.0:
resolution: {integrity: sha512-KPbL9KAB0ASvhSDbOrZBaccXS+/s7/LIofbPyERww8hM5Ko71GUJQ6Nmg0BWqj8phAIT8zdf/Sd/RftHU9i2HA==}
dev: false
@@ -19230,70 +19218,6 @@ packages:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: false
/pg-cloudflare@1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
dev: false
optional: true
/pg-connection-string@2.6.2:
resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==}
dev: false
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
dev: false
/pg-pool@3.6.1(pg@8.11.3):
resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.11.3
dev: false
/pg-protocol@1.6.0:
resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==}
dev: false
/pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
dependencies:
pg-int8: 1.0.1
postgres-array: 2.0.0
postgres-bytea: 1.0.0
postgres-date: 1.0.7
postgres-interval: 1.2.0
dev: false
/pg@8.11.3:
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
engines: {node: '>= 8.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
dependencies:
buffer-writer: 2.0.0
packet-reader: 1.0.0
pg-connection-string: 2.6.2
pg-pool: 3.6.1(pg@8.11.3)
pg-protocol: 1.6.0
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.1.1
dev: false
/pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
dependencies:
split2: 4.2.0
dev: false
/pick-util@1.1.5:
resolution: {integrity: sha512-H0MaM8T7wpQ/azvB12ChZw7kpSFzjsgv3Z+N7fUWnL1McTGSEeroCngcK4eOPiFQq08rAyKX3hadcAB1kUqfXA==}
dependencies:
@@ -19403,28 +19327,6 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
dev: false
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
dependencies:
xtend: 4.0.2
dev: false
/postman-collection@4.2.0:
resolution: {integrity: sha512-tvOLgN1h6Kab6dt43PmBoV5kYO/YUta3x0C2QqfmbzmHZe47VTpZ/+gIkGlbNhjKNPUUub5X6ehxYKoaTYdy1w==}
engines: {node: '>=10'}
@@ -20839,11 +20741,6 @@ packages:
readable-stream: 3.6.0
dev: true
/split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/split@1.0.1:
resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
dependencies:
@@ -23174,23 +23071,6 @@ packages:
- supports-color
dev: true
/vite-plugin-vue-layouts@0.7.0(vite@3.2.4)(vue-router@4.2.4)(vue@3.2.45):
resolution: {integrity: sha512-k5XDmRNFo4M/GmUjhbRXj2WmJiFcGoVI8l/uZ72RHyRDQr4wE/6Zq/KFq0lqXomWQxTSzakQRUswzNwtvZLE8A==}
peerDependencies:
vite: ^2.5.0 || ^3.0.0-0
vue: ^2.6.12 || ^3.2.4
vue-router: ^3.5.1 || ^ 4.0.11
dependencies:
'@vue/compiler-sfc': 3.3.4
debug: 4.3.4(supports-color@9.2.2)
fast-glob: 3.3.1
vite: 3.2.4(@types/node@17.0.45)(sass@1.53.0)(terser@5.19.2)
vue: 3.2.45
vue-router: 4.2.4(vue@3.2.45)
transitivePeerDependencies:
- supports-color
dev: true
/vite-plugin-vue-layouts@0.8.0(vite@4.4.9)(vue-router@4.2.4)(vue@3.3.4):
resolution: {integrity: sha512-UZW2nSV2LraTSe7gsAL46hfdi7a0X1RvkGGoJVtA2O8beu7anzpXFwQLou8+kHy31CzVycT4gIPySBsHhtBN5g==}
peerDependencies:
@@ -23894,15 +23774,6 @@ packages:
'@vue/devtools-api': 6.5.0
vue: 3.2.45
/vue-router@4.2.4(vue@3.2.45):
resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==}
peerDependencies:
vue: ^3.2.0
dependencies:
'@vue/devtools-api': 6.5.0
vue: 3.2.45
dev: true
/vue-router@4.2.4(vue@3.3.4):
resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==}
peerDependencies:

View File

@@ -18,11 +18,11 @@ RUN pnpm exec prisma generate
RUN pnpm run build
# Remove the env file to avoid backend copying it in and using it
RUN rm "../../.env"
ENV PRODUCTION="false"
ENV PRODUCTION="true"
ENV PORT=3170
ENV APP_PORT=${PORT}
ENV DB_URL=${DATABASE_URL}
CMD ["pnpm", "run", "start:dev"]
CMD ["pnpm", "run", "start:prod"]
EXPOSE 3170
FROM base_builder as fe_builder