├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── Caddyfile
├── Dockerfile
├── README.md
├── cmd
├── build_cert.go
├── build_config.go
├── generate_license.go
├── root.go
└── run_server.go
├── frontend
├── .gitignore
├── .npmrc
├── README.md
├── bun.lock
├── justfile
├── package.json
├── src
│ ├── app.d.ts
│ ├── app.html
│ ├── lib
│ │ ├── error.svelte
│ │ ├── index.ts
│ │ ├── loading.svelte
│ │ └── tailwind.css
│ └── routes
│ │ ├── +error.svelte
│ │ ├── +layout.svelte
│ │ ├── +page.svelte
│ │ ├── config
│ │ └── [type]
│ │ │ ├── +page.svelte
│ │ │ └── +page.ts
│ │ └── license
│ │ ├── +page.svelte
│ │ ├── +page.ts
│ │ └── icon.svelte
├── static
│ └── favicon.png
├── svelte.config.js
├── tsconfig.json
└── vite.config.ts
├── go.mod
├── go.sum
├── internal
├── algo
│ └── rsa.go
├── cert
│ └── cert.go
├── config
│ └── config.go
├── license
│ ├── code.go
│ └── license.go
└── util
│ └── random.go
├── justfile
├── main.go
└── server
├── config
└── config.go
├── handler
├── base.go
├── config.go
├── license.go
├── obtain_ticket.go
├── ping.go
└── release_ticket.go
├── middleware
├── certificate.go
└── middleware.go
├── router
└── router.go
└── server.go
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Multi-Platform Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | - 'master'
8 |
9 | jobs:
10 | build:
11 | name: Build for ${{ matrix.os }}
12 | runs-on: ${{ matrix.os }}
13 | strategy:
14 | matrix:
15 | os: [ubuntu-latest, windows-latest, macos-latest]
16 | include:
17 | - os: ubuntu-latest
18 | platform: linux
19 | binary_name: jetbrain-hacker_${{ github.ref_name }}_linux_amd64
20 | - os: windows-latest
21 | platform: windows
22 | binary_name: jetbrain-hacker_${{ github.ref_name }}_windows_amd64.exe
23 | - os: macos-latest
24 | platform: macos
25 | binary_name: jetbrain-hacker_${{ github.ref_name }}_darwin_amd64
26 |
27 | steps:
28 | - name: Checkout code
29 | uses: actions/checkout@v4
30 |
31 | - name: Set up Go
32 | uses: actions/setup-go@v4
33 | with:
34 | go-version: '1.24'
35 |
36 | - name: Init Go
37 | run: go mod tidy
38 |
39 | - name: Build
40 | run: go build -v -o ${{ matrix.binary_name }} .
41 |
42 | - name: Upload Artifact
43 | uses: actions/upload-artifact@v4.6.2
44 | with:
45 | name: ${{ matrix.binary_name }}
46 | path: ${{ matrix.binary_name }}
47 |
48 | - name: Release
49 | uses: softprops/action-gh-release@v2
50 | if: github.ref_type == 'tag'
51 | with:
52 | files: ${{ matrix.binary_name }}
53 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea/
--------------------------------------------------------------------------------
/Caddyfile:
--------------------------------------------------------------------------------
1 | {
2 | auto_https disable_certs
3 | }
4 |
5 | :80 {
6 | encode gzip
7 | handle_path /api/* {
8 | reverse_proxy localhost:8080
9 | }
10 | handle /rpc/* {
11 | reverse_proxy localhost:8080
12 | }
13 | reverse_proxy localhost:3000
14 | log {
15 | output file /var/log/caddy/caddy.log
16 | }
17 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:latest
2 | LABEL authors="LovesAsuna"
3 |
4 | WORKDIR /usr/src/jetbrains_hacker
5 |
6 | COPY . .
7 |
8 | RUN apt-get update \
9 | && apt-get install -y wget curl unzip
10 |
11 | RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
12 | CMD ["just"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ---
4 |
5 | # JetBrains Hacker
6 |
7 | JetBrains Hacker is a tool that can customize your JetBrains IDEs license.
8 |
9 | I create it to activate my common IDEs, and hope that it can be useful to others.
10 |
11 | ## Features
12 |
13 | - 💪 Useful - Offline activation code and online license server.
14 | - 🔨 Customize - Any information as you want, just make your own license.
15 |
16 | ## Usage
17 |
18 | 1. Get the `ja-netfilter` from the Internet. This software is worked based on the `ja-netfilter`.
19 |
20 | 2. Add -javaagent:/path/to/ja-netfilter.jar=`${app}` to your vmoptions (manual or auto). Note that `${app}` is a parameter to `ja-netfilter` that specifies the location of the `config-${app}` and `plugins-${app}` folders, if empty the `config` and `plugins` folders will be used by default.
21 |
22 |
23 | > for Java version 17+, you need add these 2 lines to your vmoptions file: (for manual, without any whitespace chars)
24 |
25 | ```vmoptions
26 | --add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
27 | --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
28 | ```
29 |
30 | 3. Get the `jetbrains hacker`. See [Installation](#installation) or [Build](#build) on bellowed.
31 |
32 | 4. There are two ways to use the `jetbrains hacker`.
33 |
34 | ### Activation Code
35 |
36 | > This approach is used in offline scenario.
37 |
38 | 1. Run `jetbrains_hacker build-cert` to build needed certificates.
39 | 2. Run `jetbrains_hacker build-config --type {power|url|dns}` to generate the corresponding configurations. Then copy the generated configurations into your `ja-netfilter` configuration files.
40 | 3. Run `jetbrains_hacker generate-license --licenseId ${licenseId} --name ${name} --user ${user} --email ${email} --time {2999-01-02}`. Or simplest of all, you can just use `jetbrain_hacker generate-license`.
41 | 4. Use the activation code in the `Activation Code` window.
42 | 5. Don't care about the activation time, it is a fallback license and will not expire.
43 |
44 | Enjoy it~
45 |
46 | ### License Server
47 |
48 | > This approach is used in online scenario.
49 |
50 | 1. Run `jetbrains_hacker run-server` to run a online license server. By default the server will run on `:80`. If you want to change the address, use the `--addr` argument.
51 | 2. Go to `${your server address}/config/{power|url|dns}` to get the corresponding configurations. Then copy the generated configurations into your `ja-netfilter` configuration files.
52 | 3. Type your server address in the `License Server` window.
53 | 4. Don't care about the activation time, it is a fallback license and will not expire.
54 |
55 | Enjoy it~
56 |
57 | ## Installation
58 |
59 | You can choose to use the pre-compiled binary or build it by yourself.
60 |
61 | ### Release Binaries
62 |
63 | [Available for download in releases](https://github.com/LovesAsuna/jetbrains_hacker/releases)
64 |
65 | Binaries available for:
66 |
67 | #### Linux
68 |
69 | - jetbrain-hacker_linux_amd64 (linux musl statically linked)
70 | - jetbrains_hacker-linux-aarch64.tar.gz (linux on 64 bit arm)
71 |
72 | All contain a single binary file
73 |
74 | #### macOS
75 |
76 | - jetbrains_hacker-mac.tar.gz (arm64)
77 | - jetbrain-hacker_darwin_amd64 (intel x86)
78 |
79 | #### Windows
80 |
81 | - jetbrain-hacker_windows_amd64.exe (single 64bit binary)
82 |
83 | ## Build
84 |
85 | ### Requirements
86 |
87 | - Minimum supported `go` version: `1.24`
88 | - See [Install Go](https://go.dev/dl/)
89 |
90 | - To build needed dependency (run `go mod tidy`)
91 |
92 | ### Go Install
93 |
94 | The simplest way to start playing around with `jetbrains_hacker` is to have `go` build and install it with `go build .`.
95 |
--------------------------------------------------------------------------------
/cmd/build_cert.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | var buildCertCmd = &cobra.Command{
10 | Use: "build-cert",
11 | Short: `Build all needed certificates.`,
12 | Long: `Build all needed certificates included user certificate and license server certificate.`,
13 | RunE: func(cmd *cobra.Command, args []string) error {
14 | if _, err := cert.GenerateFakeCertificate(
15 | cert.JetProfileCert.CommonName(),
16 | cmd.Flag("user-cert-cn").Value.String(),
17 | cmd.Flag("user-cert").Value.String(),
18 | cmd.Flag("user-key").Value.String(),
19 | ); err != nil {
20 | return err
21 | }
22 | if _, err := cert.GenerateFakeCertificate(
23 | cert.LicenseServerCert.CommonName(),
24 | fmt.Sprintf("%s.lsrv.jetbrains.com", cmd.Flag("server-uid").Value.String()),
25 | cmd.Flag("license-server-cert").Value.String(),
26 | cmd.Flag("license-server-key").Value.String(),
27 | ); err != nil {
28 | return err
29 | }
30 | fmt.Println("build cert successfully!")
31 | return nil
32 | },
33 | }
34 |
35 | func init() {
36 | rootCmd.AddCommand(buildCertCmd)
37 |
38 | buildCertCmd.Flags().StringP("user-cert-cn", "n", "localhost", "Common name of the user certificate.")
39 | buildCertCmd.Flags().StringP("server-uid", "s", "custom", "The server uid of license server.")
40 | }
41 |
--------------------------------------------------------------------------------
/cmd/build_config.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
6 | "github.com/LovesAsuna/jetbrains_hacker/internal/config"
7 | "github.com/spf13/cobra"
8 | )
9 |
10 | var buildConfigCmd = &cobra.Command{
11 | Use: "build-config",
12 | Short: `Build the *.conf of ja-netfilter.`,
13 | Long: `Build the *.conf of ja-netfilter.`,
14 | RunE: func(cmd *cobra.Command, args []string) error {
15 | switch cmd.Flag("type").Value.String() {
16 | case "dns":
17 | fmt.Println(config.BuildDnsConfig())
18 | return nil
19 | case "url":
20 | fmt.Println(config.BuildUrlConfig())
21 | return nil
22 | case "power":
23 | userCert, err := cert.CreateCertFromFileWithoutPrivateKey(cmd.Flag("user-cert").Value.String())
24 | if err != nil {
25 | return err
26 | }
27 | licenseServerCert, err := cert.CreateCertFromFileWithoutPrivateKey(cmd.Flag("license-server-cert").Value.String())
28 | if err != nil {
29 | return err
30 | }
31 | fmt.Println(
32 | config.BuildPowerConfig(
33 | [2]*cert.Certificate{
34 | userCert, cert.JetProfileCert,
35 | },
36 | [2]*cert.Certificate{
37 | licenseServerCert, cert.LicenseServerCert,
38 | },
39 | ),
40 | )
41 | default:
42 | fmt.Println("unknown config type.")
43 | }
44 | return nil
45 | },
46 | }
47 |
48 | func init() {
49 | rootCmd.AddCommand(buildConfigCmd)
50 |
51 | buildConfigCmd.Flags().StringP("type", "t", "power", "If empty use power. Possible values: 'power', 'dns', 'url'.")
52 | }
53 |
--------------------------------------------------------------------------------
/cmd/generate_license.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
8 | "github.com/LovesAsuna/jetbrains_hacker/internal/license"
9 | "github.com/LovesAsuna/jetbrains_hacker/internal/util"
10 | "github.com/dromara/carbon/v2"
11 | "github.com/lovesasuna/sync/coroutinegroup"
12 | "github.com/spf13/cobra"
13 | )
14 |
15 | var generateLicenseCmd = &cobra.Command{
16 | Use: "generate-license",
17 | Short: `generate-license.`,
18 | Long: `generate-license.`,
19 | RunE: func(cmd *cobra.Command, args []string) error {
20 | licenseId := cmd.Flag("licenseId").Value.String()
21 | codes, err := getCodes()
22 | if err != nil {
23 | return err
24 | }
25 | licenseCode, err := license.GenerateLicenseCode(
26 | cert.MustCreateCertFromFile(cmd.Flag("user-cert").Value.String(), cmd.Flag("user-key").Value.String()),
27 | licenseId,
28 | cmd.Flag("name").Value.String(),
29 | cmd.Flag("user").Value.String(),
30 | cmd.Flag("email").Value.String(),
31 | cmd.Flag("time").Value.String(),
32 | codes...,
33 | )
34 | if err != nil {
35 | return err
36 | }
37 | fmt.Println(licenseCode)
38 | return nil
39 | },
40 | }
41 |
42 | func getCodes() ([]string, error) {
43 | var (
44 | productCodes []string
45 | pluginCodes []string
46 | )
47 | group, _ := coroutinegroup.WithContext(context.Background())
48 | group.Go(
49 | func(ctx context.Context) error {
50 | codes, err := license.GetProductCode()
51 | if err != nil {
52 | return err
53 | }
54 | productCodes = codes
55 | return nil
56 | },
57 | )
58 | group.Go(
59 | func(ctx context.Context) error {
60 | codes, err := license.GetPluginCode(10000, 0, "")
61 | if err != nil {
62 | return err
63 | }
64 | pluginCodes = codes
65 | return nil
66 | },
67 | )
68 | errs := group.Wait()
69 | if len(errs) > 0 {
70 | return nil, errors.Join(errs...)
71 | }
72 | codes := make([]string, 0, len(productCodes)+len(pluginCodes))
73 | codes = append(codes, productCodes...)
74 | codes = append(codes, pluginCodes...)
75 | return codes, nil
76 | }
77 |
78 | func init() {
79 | rootCmd.AddCommand(generateLicenseCmd)
80 |
81 | generateLicenseCmd.Flags().String("licenseId", util.GetRandomString(10), "Id of license.")
82 | generateLicenseCmd.Flags().String("name", "user", "The licensee name of license.")
83 | generateLicenseCmd.Flags().String("user", "user", "The assignee name of license.")
84 | generateLicenseCmd.Flags().String("email", "i@user.com", "The assignee email of license.")
85 | generateLicenseCmd.Flags().String("time", carbon.Now().AddYears(2).SetLayout(carbon.DateLayout).String(), "The expire time of license.")
86 | }
87 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 | "os"
6 | )
7 |
8 | // rootCmd represents the base command when called without any subcommands.
9 | var rootCmd = &cobra.Command{
10 | Use: "jetbrain-hacker",
11 | Short: "Generate custom license code or run a license server.",
12 | Long: `Generate custom license code or run a license server.`,
13 | }
14 |
15 | // Execute adds all child commands to the root command and sets flags appropriately.
16 | // This is called by main.main(). It only needs to happen once to the rootCmd.
17 | func Execute() {
18 | if err := rootCmd.Execute(); err != nil {
19 | os.Exit(1)
20 | }
21 | }
22 |
23 | func init() {
24 | rootCmd.PersistentFlags().StringP("user-cert", "c", "cert/user.crt", "Path to store the user certificate.")
25 | rootCmd.PersistentFlags().StringP("user-key", "k", "cert/user.key", "Path to store the user private key.")
26 | rootCmd.PersistentFlags().String("license-server-cert", "cert/license_server.crt", "Path to store the license server certificate.")
27 | rootCmd.PersistentFlags().String("license-server-key", "cert/license_server.key", "Path to store the license server private key.")
28 | }
29 |
--------------------------------------------------------------------------------
/cmd/run_server.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/server"
5 | "github.com/LovesAsuna/jetbrains_hacker/server/config"
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | var runServerCmd = &cobra.Command{
10 | Use: "run-server",
11 | Short: `Run a JetBrain license server.`,
12 | Long: `Run a JetBrain license server.`,
13 | RunE: func(cmd *cobra.Command, args []string) error {
14 | config.InitServerConfig(
15 | &config.ServerConfig{
16 | Addr: cmd.Flag("addr").Value.String(),
17 | Licensee: cmd.Flag("licensee").Value.String(),
18 | UserCertPath: cmd.Flag("user-cert").Value.String(),
19 | UserPrivateKeyPath: cmd.Flag("user-key").Value.String(),
20 | LicenseServerCertPath: cmd.Flag("license-server-cert").Value.String(),
21 | LicenseServerPrivateKeyPath: cmd.Flag("license-server-key").Value.String(),
22 | },
23 | )
24 | return server.RunServer()
25 | },
26 | }
27 |
28 | func init() {
29 | rootCmd.AddCommand(runServerCmd)
30 |
31 | runServerCmd.Flags().String("addr", ":80", "The address of license server.")
32 | runServerCmd.Flags().String("licensee", "", "The licensee of license server. Default to computer user name")
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # Output
4 | .output
5 | .vercel
6 | .netlify
7 | .wrangler
8 | /.svelte-kit
9 | /build
10 |
11 | # OS
12 | .DS_Store
13 | Thumbs.db
14 |
15 | # Env
16 | .env
17 | .env.*
18 | !.env.example
19 | !.env.test
20 |
21 | # Vite
22 | vite.config.js.timestamp-*
23 | vite.config.ts.timestamp-*
24 |
25 | *.iml
26 | .idea/
--------------------------------------------------------------------------------
/frontend/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # sv
2 |
3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
4 |
5 | ## Creating a project
6 |
7 | If you're seeing this, you've probably already done this step. Congrats!
8 |
9 | ```bash
10 | # create a new project in the current directory
11 | npx sv create
12 |
13 | # create a new project in my-app
14 | npx sv create my-app
15 | ```
16 |
17 | ## Developing
18 |
19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
20 |
21 | ```bash
22 | npm run dev
23 |
24 | # or start the server and open the app in a new browser tab
25 | npm run dev -- --open
26 | ```
27 |
28 | ## Building
29 |
30 | To create a production version of your app:
31 |
32 | ```bash
33 | npm run build
34 | ```
35 |
36 | You can preview the production build with `npm run preview`.
37 |
38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
39 |
--------------------------------------------------------------------------------
/frontend/bun.lock:
--------------------------------------------------------------------------------
1 | {
2 | "lockfileVersion": 1,
3 | "workspaces": {
4 | "": {
5 | "name": "frontend",
6 | "devDependencies": {
7 | "@sveltejs/adapter-auto": "^6.0.0",
8 | "@sveltejs/adapter-node": "^5.2.12",
9 | "@sveltejs/adapter-static": "^3.0.8",
10 | "@sveltejs/kit": "^2.16.0",
11 | "@sveltejs/vite-plugin-svelte": "^5.0.0",
12 | "@tailwindcss/vite": "^4.1.6",
13 | "svelte": "^5.0.0",
14 | "svelte-check": "^4.0.0",
15 | "tailwindcss": "^4.1.6",
16 | "typescript": "^5.0.0",
17 | "vite": "^6.3.5",
18 | },
19 | },
20 | },
21 | "packages": {
22 | "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
23 |
24 | "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ=="],
25 |
26 | "@esbuild/android-arm": ["@esbuild/android-arm@0.25.3", "", { "os": "android", "cpu": "arm" }, "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A=="],
27 |
28 | "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.3", "", { "os": "android", "cpu": "arm64" }, "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ=="],
29 |
30 | "@esbuild/android-x64": ["@esbuild/android-x64@0.25.3", "", { "os": "android", "cpu": "x64" }, "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ=="],
31 |
32 | "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w=="],
33 |
34 | "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A=="],
35 |
36 | "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw=="],
37 |
38 | "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q=="],
39 |
40 | "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.3", "", { "os": "linux", "cpu": "arm" }, "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ=="],
41 |
42 | "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A=="],
43 |
44 | "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw=="],
45 |
46 | "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g=="],
47 |
48 | "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag=="],
49 |
50 | "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg=="],
51 |
52 | "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA=="],
53 |
54 | "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ=="],
55 |
56 | "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.3", "", { "os": "linux", "cpu": "x64" }, "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA=="],
57 |
58 | "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.3", "", { "os": "none", "cpu": "arm64" }, "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA=="],
59 |
60 | "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.3", "", { "os": "none", "cpu": "x64" }, "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g=="],
61 |
62 | "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ=="],
63 |
64 | "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w=="],
65 |
66 | "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA=="],
67 |
68 | "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ=="],
69 |
70 | "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew=="],
71 |
72 | "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.3", "", { "os": "win32", "cpu": "x64" }, "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg=="],
73 |
74 | "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
75 |
76 | "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
77 |
78 | "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
79 |
80 | "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
81 |
82 | "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
83 |
84 | "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
85 |
86 | "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
87 |
88 | "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ=="],
89 |
90 | "@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="],
91 |
92 | "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA=="],
93 |
94 | "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
95 |
96 | "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.1", "", { "os": "android", "cpu": "arm" }, "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw=="],
97 |
98 | "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.40.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw=="],
99 |
100 | "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.40.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA=="],
101 |
102 | "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.40.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw=="],
103 |
104 | "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.40.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw=="],
105 |
106 | "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.40.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q=="],
107 |
108 | "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.40.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg=="],
109 |
110 | "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.40.1", "", { "os": "linux", "cpu": "arm" }, "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg=="],
111 |
112 | "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.40.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg=="],
113 |
114 | "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.40.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ=="],
115 |
116 | "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ=="],
117 |
118 | "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.40.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg=="],
119 |
120 | "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ=="],
121 |
122 | "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA=="],
123 |
124 | "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.40.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg=="],
125 |
126 | "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.40.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ=="],
127 |
128 | "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.40.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ=="],
129 |
130 | "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.40.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg=="],
131 |
132 | "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.40.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA=="],
133 |
134 | "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.40.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA=="],
135 |
136 | "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="],
137 |
138 | "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.0.0", "", { "dependencies": { "import-meta-resolve": "^4.1.0" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7mR2/G7vlXakaOj6QBSG9dwBfTgWjV+UnEMB5Z6Xu0ZbdXda6c0su1fNkg0ab0zlilSkloMA2NjCna02/DR7sA=="],
139 |
140 | "@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.2.12", "", { "dependencies": { "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-0bp4Yb3jKIEcZWVcJC/L1xXp9zzJS4hDwfb4VITAkfT4OVdkspSHsx7YhqJDbb2hgLl6R9Vs7VQR+fqIVOxPUQ=="],
141 |
142 | "@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.8", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg=="],
143 |
144 | "@sveltejs/kit": ["@sveltejs/kit@2.20.8", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-ep9qTxL7WALhfm0kFecL3VHeuNew8IccbYGqv5TqL/KSqWRKzEgDG8blNlIu1CkLTTua/kHjI+f5T8eCmWIxKw=="],
145 |
146 | "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.0.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.0", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.15", "vitefu": "^1.0.4" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw=="],
147 |
148 | "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
149 |
150 | "@tailwindcss/node": ["@tailwindcss/node@4.1.6", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.6" } }, "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg=="],
151 |
152 | "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.6", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.6", "@tailwindcss/oxide-darwin-arm64": "4.1.6", "@tailwindcss/oxide-darwin-x64": "4.1.6", "@tailwindcss/oxide-freebsd-x64": "4.1.6", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6", "@tailwindcss/oxide-linux-arm64-musl": "4.1.6", "@tailwindcss/oxide-linux-x64-gnu": "4.1.6", "@tailwindcss/oxide-linux-x64-musl": "4.1.6", "@tailwindcss/oxide-wasm32-wasi": "4.1.6", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6", "@tailwindcss/oxide-win32-x64-msvc": "4.1.6" } }, "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA=="],
153 |
154 | "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.6", "", { "os": "android", "cpu": "arm64" }, "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ=="],
155 |
156 | "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ=="],
157 |
158 | "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ=="],
159 |
160 | "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w=="],
161 |
162 | "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.6", "", { "os": "linux", "cpu": "arm" }, "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA=="],
163 |
164 | "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ=="],
165 |
166 | "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw=="],
167 |
168 | "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg=="],
169 |
170 | "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A=="],
171 |
172 | "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.6", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.9", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ=="],
173 |
174 | "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg=="],
175 |
176 | "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ=="],
177 |
178 | "@tailwindcss/vite": ["@tailwindcss/vite@4.1.6", "", { "dependencies": { "@tailwindcss/node": "4.1.6", "@tailwindcss/oxide": "4.1.6", "tailwindcss": "4.1.6" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-zjtqjDeY1w3g2beYQtrMAf51n5G7o+UwmyOjtsDMP7t6XyoRMOidcoKP32ps7AkNOHIXEOK0bhIC05dj8oJp4w=="],
179 |
180 | "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
181 |
182 | "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
183 |
184 | "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
185 |
186 | "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
187 |
188 | "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
189 |
190 | "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
191 |
192 | "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
193 |
194 | "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
195 |
196 | "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
197 |
198 | "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="],
199 |
200 | "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
201 |
202 | "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
203 |
204 | "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
205 |
206 | "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
207 |
208 | "devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="],
209 |
210 | "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="],
211 |
212 | "esbuild": ["esbuild@0.25.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.3", "@esbuild/android-arm": "0.25.3", "@esbuild/android-arm64": "0.25.3", "@esbuild/android-x64": "0.25.3", "@esbuild/darwin-arm64": "0.25.3", "@esbuild/darwin-x64": "0.25.3", "@esbuild/freebsd-arm64": "0.25.3", "@esbuild/freebsd-x64": "0.25.3", "@esbuild/linux-arm": "0.25.3", "@esbuild/linux-arm64": "0.25.3", "@esbuild/linux-ia32": "0.25.3", "@esbuild/linux-loong64": "0.25.3", "@esbuild/linux-mips64el": "0.25.3", "@esbuild/linux-ppc64": "0.25.3", "@esbuild/linux-riscv64": "0.25.3", "@esbuild/linux-s390x": "0.25.3", "@esbuild/linux-x64": "0.25.3", "@esbuild/netbsd-arm64": "0.25.3", "@esbuild/netbsd-x64": "0.25.3", "@esbuild/openbsd-arm64": "0.25.3", "@esbuild/openbsd-x64": "0.25.3", "@esbuild/sunos-x64": "0.25.3", "@esbuild/win32-arm64": "0.25.3", "@esbuild/win32-ia32": "0.25.3", "@esbuild/win32-x64": "0.25.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q=="],
213 |
214 | "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
215 |
216 | "esrap": ["esrap@1.4.6", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw=="],
217 |
218 | "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
219 |
220 | "fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
221 |
222 | "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
223 |
224 | "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
225 |
226 | "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
227 |
228 | "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
229 |
230 | "import-meta-resolve": ["import-meta-resolve@4.1.0", "", {}, "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw=="],
231 |
232 | "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
233 |
234 | "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="],
235 |
236 | "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
237 |
238 | "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="],
239 |
240 | "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
241 |
242 | "lightningcss": ["lightningcss@1.29.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.29.2", "lightningcss-darwin-x64": "1.29.2", "lightningcss-freebsd-x64": "1.29.2", "lightningcss-linux-arm-gnueabihf": "1.29.2", "lightningcss-linux-arm64-gnu": "1.29.2", "lightningcss-linux-arm64-musl": "1.29.2", "lightningcss-linux-x64-gnu": "1.29.2", "lightningcss-linux-x64-musl": "1.29.2", "lightningcss-win32-arm64-msvc": "1.29.2", "lightningcss-win32-x64-msvc": "1.29.2" } }, "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA=="],
243 |
244 | "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.29.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA=="],
245 |
246 | "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.29.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w=="],
247 |
248 | "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.29.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg=="],
249 |
250 | "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.29.2", "", { "os": "linux", "cpu": "arm" }, "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg=="],
251 |
252 | "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ=="],
253 |
254 | "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.29.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ=="],
255 |
256 | "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg=="],
257 |
258 | "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.29.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w=="],
259 |
260 | "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.29.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw=="],
261 |
262 | "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
263 |
264 | "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
265 |
266 | "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
267 |
268 | "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
269 |
270 | "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
271 |
272 | "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
273 |
274 | "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
275 |
276 | "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
277 |
278 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
279 |
280 | "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
281 |
282 | "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
283 |
284 | "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
285 |
286 | "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
287 |
288 | "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
289 |
290 | "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
291 |
292 | "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
293 |
294 | "rollup": ["rollup@4.40.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.1", "@rollup/rollup-android-arm64": "4.40.1", "@rollup/rollup-darwin-arm64": "4.40.1", "@rollup/rollup-darwin-x64": "4.40.1", "@rollup/rollup-freebsd-arm64": "4.40.1", "@rollup/rollup-freebsd-x64": "4.40.1", "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", "@rollup/rollup-linux-arm-musleabihf": "4.40.1", "@rollup/rollup-linux-arm64-gnu": "4.40.1", "@rollup/rollup-linux-arm64-musl": "4.40.1", "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", "@rollup/rollup-linux-riscv64-gnu": "4.40.1", "@rollup/rollup-linux-riscv64-musl": "4.40.1", "@rollup/rollup-linux-s390x-gnu": "4.40.1", "@rollup/rollup-linux-x64-gnu": "4.40.1", "@rollup/rollup-linux-x64-musl": "4.40.1", "@rollup/rollup-win32-arm64-msvc": "4.40.1", "@rollup/rollup-win32-ia32-msvc": "4.40.1", "@rollup/rollup-win32-x64-msvc": "4.40.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw=="],
295 |
296 | "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
297 |
298 | "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="],
299 |
300 | "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="],
301 |
302 | "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
303 |
304 | "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
305 |
306 | "svelte": ["svelte@5.28.2", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg=="],
307 |
308 | "svelte-check": ["svelte-check@4.1.7", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1jX4BzXrQJhC/Jt3SqYf6Ntu//vmfc6VWp07JkRfK2nn+22yIblspVUo96gzMkg0Zov8lQicxhxsMzOctwcMQQ=="],
309 |
310 | "tailwindcss": ["tailwindcss@4.1.6", "", {}, "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg=="],
311 |
312 | "tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
313 |
314 | "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
315 |
316 | "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
317 |
318 | "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
319 |
320 | "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
321 |
322 | "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
323 |
324 | "vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="],
325 |
326 | "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
327 |
328 | "zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="],
329 |
330 | "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
331 |
332 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
333 |
334 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
335 |
336 | "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
337 |
338 | "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="],
339 |
340 | "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
341 |
342 | "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/frontend/justfile:
--------------------------------------------------------------------------------
1 | frontend: build_frontend
2 | #!/bin/env bash
3 | source ~/.bashrc
4 | bun run build/ &
5 |
6 | build_frontend: bun
7 | #!/bin/env bash
8 | source ~/.bashrc
9 | bun install
10 | bun run build
11 |
12 | bun:
13 | #!/bin/env bash
14 | curl -fsSL https://bun.sh/install | bash
15 | source ~/.bashrc
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.1",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite dev",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "prepare": "svelte-kit sync || echo ''",
11 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
13 | },
14 | "devDependencies": {
15 | "@sveltejs/adapter-auto": "^6.0.0",
16 | "@sveltejs/kit": "^2.16.0",
17 | "@sveltejs/vite-plugin-svelte": "^5.0.0",
18 | "svelte": "^5.0.0",
19 | "svelte-check": "^4.0.0",
20 | "typescript": "^5.0.0",
21 | "vite": "^6.3.5",
22 | "@sveltejs/adapter-node": "^5.2.12",
23 | "@sveltejs/adapter-static": "^3.0.8",
24 | "@tailwindcss/vite": "^4.1.6",
25 | "tailwindcss": "^4.1.6"
26 | }
27 | }
--------------------------------------------------------------------------------
/frontend/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://svelte.dev/docs/kit/types#app.d.ts
2 | // for information about these interfaces
3 | declare global {
4 | namespace App {
5 | // interface Error {}
6 | // interface Locals {}
7 | // interface PageData {}
8 | // interface PageState {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/frontend/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/frontend/src/lib/error.svelte:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | // place files you want to import through the `$lib` alias in this folder.
2 |
--------------------------------------------------------------------------------
/frontend/src/lib/loading.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Loading data...
5 |
6 |
7 |
--------------------------------------------------------------------------------
/frontend/src/lib/tailwind.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
--------------------------------------------------------------------------------
/frontend/src/routes/+error.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/frontend/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | {@render children()}
6 |
7 |
--------------------------------------------------------------------------------
/frontend/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | JetBrains_Hacker
5 |
6 |
7 | This is a
8 |
9 | JetBrains
10 |
11 | license server.
12 |
13 |
It provides a license server for JetBrains IDEs.
14 |
Documentation:
15 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/frontend/src/routes/config/[type]/+page.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 | {#await data.loader()}
18 |
19 | {:then c }
20 |
21 | Copy Config
22 | {:catch _}
23 |
24 | {/await}
25 |
26 |
--------------------------------------------------------------------------------
/frontend/src/routes/config/[type]/+page.ts:
--------------------------------------------------------------------------------
1 | import type {PageLoad} from './$types';
2 |
3 | export type Config = {
4 | type: string;
5 | config: string;
6 | }
7 |
8 | export const load: PageLoad = ({params, fetch}) => {
9 | return {
10 | loader: async function() {
11 | let response = await fetch(`/api/config/${params.type}`);
12 | let text = await response.text();
13 | return JSON.parse(text);
14 | }
15 | }
16 | };
--------------------------------------------------------------------------------
/frontend/src/routes/license/+page.svelte:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Download jetbra.zip , and configure as described in
49 | readme.txt ! For testing purposes only, not for
50 | commercial use!
51 | Please note that this is just a personal page, not an official website!
52 |
53 |
54 |
55 |
57 | {#each productList as product}
58 |
61 |
83 |
84 |
{product.name}
86 |
{copyLicense(event, product.code)}}
91 | data-content="Copy to clipboard">
92 | *********************************************************************************************************************************************************
93 |
94 |
95 |
96 |
97 |
98 | {/each}
99 |
100 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/frontend/src/routes/license/+page.ts:
--------------------------------------------------------------------------------
1 | import type {PageLoad} from './$types';
2 |
3 | export interface ProductDto {
4 | code: string,
5 | salesCode: string,
6 | name: string,
7 | description: string,
8 | forSale: boolean
9 | productFamilyName: string
10 | releases: Array<{ version: string }>
11 | }
12 |
13 | export interface Product {
14 | code: string,
15 | name: string,
16 | productFamilyName: string
17 | version: string
18 | }
19 |
20 | const DataBaseUrl = "https://data.services.jetbrains.com"
21 | export const load: PageLoad = async ({fetch}) => {
22 | let resp = await fetch(`${DataBaseUrl}/products?fields=name,code,forSale,salesCode,description,productFamilyName,releases.version`)
23 | let productDtos: Array = await resp.json()
24 | let products: Array = [];
25 | productDtos.forEach(dto => {
26 | dto.productFamilyName = dto.productFamilyName.replace(" ", "-").toLowerCase()
27 | let extra = dto.forSale && (dto.salesCode != dto.code);
28 | products.push(
29 | {
30 | code: dto.code,
31 | name: extra ? `${dto.name}(${dto.code})` : dto.name,
32 | productFamilyName: dto.productFamilyName,
33 | version: dto.releases.length > 0 ? dto.releases[0].version: ""
34 | }
35 | );
36 | if (extra) {
37 | products.push(
38 | {
39 | code: dto.salesCode,
40 | name: `${dto.name}(${dto.salesCode})`,
41 | productFamilyName: dto.productFamilyName,
42 | version: dto.releases.length > 0 ? dto.releases[0].version: ""
43 | }
44 | );
45 | }
46 | });
47 | return {
48 | products
49 | }
50 | };
--------------------------------------------------------------------------------
/frontend/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LovesAsuna/jetbrains_hacker/c71a12145d44132dc93987ba7993ec048ef8a13d/frontend/static/favicon.png
--------------------------------------------------------------------------------
/frontend/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-node';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://svelte.dev/docs/kit/integrations
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13 | // See https://svelte.dev/docs/kit/adapters for more information about adapters.
14 | adapter: adapter()
15 | }
16 | };
17 |
18 | export default config;
19 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "moduleResolution": "bundler"
13 | }
14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
16 | //
17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18 | // from the referenced tsconfig.json - TypeScript does not merge them in
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 | import tailwindcss from '@tailwindcss/vite';
4 |
5 | export default defineConfig({
6 | plugins: [sveltekit(), tailwindcss()]
7 | });
8 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/LovesAsuna/jetbrains_hacker
2 |
3 | go 1.24
4 |
5 | require (
6 | github.com/dromara/carbon/v2 v2.6.3
7 | github.com/gin-gonic/gin v1.10.0
8 | github.com/spf13/cobra v1.9.1
9 | )
10 |
11 | require (
12 | github.com/bytedance/sonic v1.13.2 // indirect
13 | github.com/bytedance/sonic/loader v0.2.4 // indirect
14 | github.com/cloudwego/base64x v0.1.5 // indirect
15 | github.com/cloudwego/iasm v0.2.0 // indirect
16 | github.com/gabriel-vasile/mimetype v1.4.9 // indirect
17 | github.com/gin-contrib/sse v1.1.0 // indirect
18 | github.com/go-playground/locales v0.14.1 // indirect
19 | github.com/go-playground/universal-translator v0.18.1 // indirect
20 | github.com/go-playground/validator/v10 v10.26.0 // indirect
21 | github.com/goccy/go-json v0.10.5 // indirect
22 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
23 | github.com/json-iterator/go v1.1.12 // indirect
24 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect
25 | github.com/leodido/go-urn v1.4.0 // indirect
26 | github.com/lovesasuna/sync v0.0.0-20250409122049-8bfa10ab236c // indirect
27 | github.com/mattn/go-isatty v0.0.20 // indirect
28 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
29 | github.com/modern-go/reflect2 v1.0.2 // indirect
30 | github.com/pelletier/go-toml/v2 v2.2.4 // indirect
31 | github.com/spf13/pflag v1.0.6 // indirect
32 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
33 | github.com/ugorji/go/codec v1.2.12 // indirect
34 | golang.org/x/arch v0.16.0 // indirect
35 | golang.org/x/crypto v0.37.0 // indirect
36 | golang.org/x/net v0.39.0 // indirect
37 | golang.org/x/sys v0.32.0 // indirect
38 | golang.org/x/text v0.24.0 // indirect
39 | google.golang.org/protobuf v1.36.6 // indirect
40 | gopkg.in/yaml.v3 v3.0.1 // indirect
41 | )
42 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
2 | github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
3 | github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
4 | github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
5 | github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
6 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
7 | github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
8 | github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
9 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
10 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
11 | github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
12 | github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
13 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
14 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
15 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
16 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18 | github.com/dromara/carbon/v2 v2.6.2 h1:ETogW/+yLDJfiSYyG74uuN3yN6NvLTC7E0Vi5WKyJuo=
19 | github.com/dromara/carbon/v2 v2.6.2/go.mod h1:Baj3A1uBBctJmpZWJd6/+WWnmIuY2pobR6IOpB6xigc=
20 | github.com/dromara/carbon/v2 v2.6.3 h1:suYXDpa4/xNUtg0cqMu3rp6sQW8ZAmX+F9qi9k+vogw=
21 | github.com/dromara/carbon/v2 v2.6.3/go.mod h1:Baj3A1uBBctJmpZWJd6/+WWnmIuY2pobR6IOpB6xigc=
22 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
23 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
24 | github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
25 | github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
26 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
27 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
28 | github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
29 | github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
30 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
31 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
32 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
33 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
34 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
35 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
36 | github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
37 | github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
38 | github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
39 | github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
40 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
41 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
42 | github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
43 | github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
44 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
45 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
46 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
47 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
48 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
49 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
50 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
51 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
52 | github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
53 | github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
54 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
55 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
56 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
57 | github.com/lovesasuna/sync v0.0.0-20250409122049-8bfa10ab236c h1:uQKJLcIIgvnYSMxjGLEEYiN0gUrAnt4SKmldwkaFNVo=
58 | github.com/lovesasuna/sync v0.0.0-20250409122049-8bfa10ab236c/go.mod h1:XJIk9szX480mSUo019HXuVSQcM4tQaDViIxmc8ct48k=
59 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
60 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
61 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
62 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
63 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
64 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
65 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
66 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
67 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
68 | github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
69 | github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
70 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
71 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
72 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
73 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
74 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
75 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
76 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
77 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
78 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
79 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
80 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
81 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
82 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
83 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
84 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
85 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
86 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
87 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
88 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
89 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
90 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
91 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
92 | golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
93 | golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
94 | golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
95 | golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
96 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
97 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
98 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
99 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
100 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
101 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
102 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
103 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
104 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
105 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
106 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
107 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
108 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
109 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
110 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
111 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
112 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
113 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
114 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
115 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
116 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
117 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
118 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
119 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
120 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
121 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
122 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
123 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
124 |
--------------------------------------------------------------------------------
/internal/algo/rsa.go:
--------------------------------------------------------------------------------
1 | package algo
2 |
3 | import (
4 | "crypto/rsa"
5 | "crypto/sha256"
6 | "errors"
7 | )
8 |
9 | // The following code is copied from the standard library
10 | var hashPrefixes = map[string][]byte{
11 | "SHA-256": {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
12 | }
13 |
14 | var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA key size")
15 |
16 | func pkcs1v15ConstructEM(pub *rsa.PublicKey, hash string, hashed []byte) ([]byte, error) {
17 | // Special case: "" is used to indicate that the data is signed directly.
18 | var prefix []byte
19 | if hash != "" {
20 | var ok bool
21 | prefix, ok = hashPrefixes[hash]
22 | if !ok {
23 | return nil, errors.New("crypto/rsa: unsupported hash function")
24 | }
25 | }
26 |
27 | // EM = 0x00 || 0x01 || PS || 0x00 || T
28 | k := pub.Size()
29 | if k < len(prefix)+len(hashed)+2+8+1 {
30 | return nil, ErrMessageTooLong
31 | }
32 | em := make([]byte, k)
33 | em[1] = 1
34 | for i := 2; i < k-len(prefix)-len(hashed)-1; i++ {
35 | em[i] = 0xff
36 | }
37 | copy(em[k-len(prefix)-len(hashed):], prefix)
38 | copy(em[k-len(hashed):], hashed)
39 | return em, nil
40 | }
41 |
42 | func GetEM(pub *rsa.PublicKey, tbsCertificate []byte) ([]byte, error) {
43 | h := sha256.New()
44 | h.Write(tbsCertificate)
45 | signed := h.Sum(nil)
46 | return pkcs1v15ConstructEM(pub, "SHA-256", signed)
47 | }
48 |
--------------------------------------------------------------------------------
/internal/cert/cert.go:
--------------------------------------------------------------------------------
1 | package cert
2 |
3 | import (
4 | "crypto"
5 | "crypto/rand"
6 | "crypto/rsa"
7 | "crypto/x509"
8 | "crypto/x509/pkix"
9 | "encoding/base64"
10 | "encoding/pem"
11 | "errors"
12 | "github.com/dromara/carbon/v2"
13 | "math/big"
14 | "os"
15 | "path/filepath"
16 | "strings"
17 | )
18 |
19 | const JetProfileCertStr = `-----BEGIN CERTIFICATE-----
20 | MIIFOzCCAyOgAwIBAgIJANJssYOyg3nhMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV
21 | BAMMDUpldFByb2ZpbGUgQ0EwHhcNMTUxMDAyMTEwMDU2WhcNNDUxMDI0MTEwMDU2
22 | WjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMIICIjANBgkqhkiG9w0BAQEFAAOC
23 | Ag8AMIICCgKCAgEA0tQuEA8784NabB1+T2XBhpB+2P1qjewHiSajAV8dfIeWJOYG
24 | y+ShXiuedj8rL8VCdU+yH7Ux/6IvTcT3nwM/E/3rjJIgLnbZNerFm15Eez+XpWBl
25 | m5fDBJhEGhPc89Y31GpTzW0vCLmhJ44XwvYPntWxYISUrqeR3zoUQrCEp1C6mXNX
26 | EpqIGIVbJ6JVa/YI+pwbfuP51o0ZtF2rzvgfPzKtkpYQ7m7KgA8g8ktRXyNrz8bo
27 | iwg7RRPeqs4uL/RK8d2KLpgLqcAB9WDpcEQzPWegbDrFO1F3z4UVNH6hrMfOLGVA
28 | xoiQhNFhZj6RumBXlPS0rmCOCkUkWrDr3l6Z3spUVgoeea+QdX682j6t7JnakaOw
29 | jzwY777SrZoi9mFFpLVhfb4haq4IWyKSHR3/0BlWXgcgI6w6LXm+V+ZgLVDON52F
30 | LcxnfftaBJz2yclEwBohq38rYEpb+28+JBvHJYqcZRaldHYLjjmb8XXvf2MyFeXr
31 | SopYkdzCvzmiEJAewrEbPUaTllogUQmnv7Rv9sZ9jfdJ/cEn8e7GSGjHIbnjV2ZM
32 | Q9vTpWjvsT/cqatbxzdBo/iEg5i9yohOC9aBfpIHPXFw+fEj7VLvktxZY6qThYXR
33 | Rus1WErPgxDzVpNp+4gXovAYOxsZak5oTV74ynv1aQ93HSndGkKUE/qA/JECAwEA
34 | AaOBhzCBhDAdBgNVHQ4EFgQUo562SGdCEjZBvW3gubSgUouX8bMwSAYDVR0jBEEw
35 | P4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2Zp
36 | bGUgQ0GCCQDSbLGDsoN54TAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
37 | hkiG9w0BAQsFAAOCAgEAjrPAZ4xC7sNiSSqh69s3KJD3Ti4etaxcrSnD7r9rJYpK
38 | BMviCKZRKFbLv+iaF5JK5QWuWdlgA37ol7mLeoF7aIA9b60Ag2OpgRICRG79QY7o
39 | uLviF/yRMqm6yno7NYkGLd61e5Huu+BfT459MWG9RVkG/DY0sGfkyTHJS5xrjBV6
40 | hjLG0lf3orwqOlqSNRmhvn9sMzwAP3ILLM5VJC5jNF1zAk0jrqKz64vuA8PLJZlL
41 | S9TZJIYwdesCGfnN2AETvzf3qxLcGTF038zKOHUMnjZuFW1ba/12fDK5GJ4i5y+n
42 | fDWVZVUDYOPUixEZ1cwzmf9Tx3hR8tRjMWQmHixcNC8XEkVfztID5XeHtDeQ+uPk
43 | X+jTDXbRb+77BP6n41briXhm57AwUI3TqqJFvoiFyx5JvVWG3ZqlVaeU/U9e0gxn
44 | 8qyR+ZA3BGbtUSDDs8LDnE67URzK+L+q0F2BC758lSPNB2qsJeQ63bYyzf0du3wB
45 | /gb2+xJijAvscU3KgNpkxfGklvJD/oDUIqZQAnNcHe7QEf8iG2WqaMJIyXZlW3me
46 | 0rn+cgvxHPt6N4EBh5GgNZR4l0eaFEV+fxVsydOQYo1RIyFMXtafFBqQl6DDxujl
47 | FeU3FZ+Bcp12t7dlM4E0/sS1XdL47CfGVj4Bp+/VbF862HmkAbd7shs7sDQkHbU=
48 | -----END CERTIFICATE-----`
49 |
50 | const LicenseServerCertStr = `-----BEGIN CERTIFICATE-----
51 | MIIFTDCCAzSgAwIBAgIJAMCrW9HV+hjZMA0GCSqGSIb3DQEBCwUAMB0xGzAZBgNV
52 | BAMMEkxpY2Vuc2UgU2VydmVycyBDQTAgFw0xNjEwMTIxNDMwNTRaGA8yMTE2MTIy
53 | NzE0MzA1NFowHTEbMBkGA1UEAwwSTGljZW5zZSBTZXJ2ZXJzIENBMIICIjANBgkq
54 | hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoT7LvHj3JKK2pgc5f02z+xEiJDcvlBi6
55 | fIwrg/504UaMx3xWXAE5CEPelFty+QPRJnTNnSxqKQQmg2s/5tMJpL9lzGwXaV7a
56 | rrcsEDbzV4el5mIXUnk77Bm/QVv48s63iQqUjVmvjQt9SWG2J7+h6X3ICRvF1sQB
57 | yeat/cO7tkpz1aXXbvbAws7/3dXLTgAZTAmBXWNEZHVUTcwSg2IziYxL8HRFOH0+
58 | GMBhHqa0ySmF1UTnTV4atIXrvjpABsoUvGxw+qOO2qnwe6ENEFWFz1a7pryVOHXg
59 | P+4JyPkI1hdAhAqT2kOKbTHvlXDMUaxAPlriOVw+vaIjIVlNHpBGhqTj1aqfJpLj
60 | qfDFcuqQSI4O1W5tVPRNFrjr74nDwLDZnOF+oSy4E1/WhL85FfP3IeQAIHdswNMJ
61 | y+RdkPZCfXzSUhBKRtiM+yjpIn5RBY+8z+9yeGocoxPf7l0or3YF4GUpud202zgy
62 | Y3sJqEsZksB750M0hx+vMMC9GD5nkzm9BykJS25hZOSsRNhX9InPWYYIi6mFm8QA
63 | 2Dnv8wxAwt2tDNgqa0v/N8OxHglPcK/VO9kXrUBtwCIfZigO//N3hqzfRNbTv/ZO
64 | k9lArqGtcu1hSa78U4fuu7lIHi+u5rgXbB6HMVT3g5GQ1L9xxT1xad76k2EGEi3F
65 | 9B+tSrvru70CAwEAAaOBjDCBiTAdBgNVHQ4EFgQUpsRiEz+uvh6TsQqurtwXMd4J
66 | 8VEwTQYDVR0jBEYwRIAUpsRiEz+uvh6TsQqurtwXMd4J8VGhIaQfMB0xGzAZBgNV
67 | BAMMEkxpY2Vuc2UgU2VydmVycyBDQYIJAMCrW9HV+hjZMAwGA1UdEwQFMAMBAf8w
68 | CwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQCJ9+GQWvBS3zsgPB+1PCVc
69 | oG6FY87N6nb3ZgNTHrUMNYdo7FDeol2DSB4wh/6rsP9Z4FqVlpGkckB+QHCvqU+d
70 | rYPe6QWHIb1kE8ftTnwapj/ZaBtF80NWUfYBER/9c6To5moW63O7q6cmKgaGk6zv
71 | St2IhwNdTX0Q5cib9ytE4XROeVwPUn6RdU/+AVqSOspSMc1WQxkPVGRF7HPCoGhd
72 | vqebbYhpahiMWfClEuv1I37gJaRtsoNpx3f/jleoC/vDvXjAznfO497YTf/GgSM2
73 | LCnVtpPQQ2vQbOfTjaBYO2MpibQlYpbkbjkd5ZcO5U5PGrQpPFrWcylz7eUC3c05
74 | UVeygGIthsA/0hMCioYz4UjWTgi9NQLbhVkfmVQ5lCVxTotyBzoubh3FBz+wq2Qt
75 | iElsBrCMR7UwmIu79UYzmLGt3/gBdHxaImrT9SQ8uqzP5eit54LlGbvGekVdAL5l
76 | DFwPcSB1IKauXZvi1DwFGPeemcSAndy+Uoqw5XGRqE6jBxS7XVI7/4BSMDDRBz1u
77 | a+JMGZXS8yyYT+7HdsybfsZLvkVmc9zVSDI7/MjVPdk6h0sLn+vuPC1bIi5edoNy
78 | PdiG2uPH5eDO6INcisyPpLS4yFKliaO4Jjap7yzLU9pbItoWgCAYa2NpxuxHJ0tB
79 | 7tlDFnvaRnQukqSG+VqNWg==
80 | -----END CERTIFICATE-----`
81 |
82 | var (
83 | JetProfileCert *Certificate
84 | LicenseServerCert *Certificate
85 | )
86 |
87 | func init() {
88 | var err error
89 | JetProfileCert, err = CreateCertFromPem([]byte(JetProfileCertStr))
90 | if err != nil {
91 | panic(err)
92 | }
93 | LicenseServerCert, err = CreateCertFromPem([]byte(LicenseServerCertStr))
94 | if err != nil {
95 | panic(err)
96 | }
97 | }
98 |
99 | type Certificate struct {
100 | parent *Certificate
101 | cert *x509.Certificate
102 | privateKey *rsa.PrivateKey
103 | }
104 |
105 | func (c *Certificate) WriteCertToFile(path string) error {
106 | if c.cert == nil {
107 | return errors.New("certificate is nil")
108 | }
109 | certificatePEM := &pem.Block{
110 | Type: "CERTIFICATE",
111 | Bytes: c.cert.Raw,
112 | }
113 | bytes := pem.EncodeToMemory(certificatePEM)
114 | dir := filepath.Dir(path)
115 | _ = os.MkdirAll(dir, 0750)
116 | return os.WriteFile(path, bytes, 0666)
117 | }
118 |
119 | func (c *Certificate) WritePrivateKeyToFile(path string) error {
120 | if c.privateKey == nil {
121 | return errors.New("private key is nil")
122 | }
123 | privateKeyPEM := &pem.Block{
124 | Type: "RSA PRIVATE KEY",
125 | Bytes: x509.MarshalPKCS1PrivateKey(c.privateKey),
126 | }
127 | bytes := pem.EncodeToMemory(privateKeyPEM)
128 | dir := filepath.Dir(path)
129 | _ = os.MkdirAll(dir, 0750)
130 | return os.WriteFile(path, bytes, 0666)
131 | }
132 |
133 | func (c *Certificate) Sign(hashAlgo crypto.Hash, content []byte) ([]byte, error) {
134 | if c.privateKey == nil {
135 | return nil, errors.New("private key is nil")
136 | }
137 | sha := hashAlgo.New()
138 | sha.Write(content)
139 | hashed := sha.Sum(nil)
140 | return rsa.SignPKCS1v15(rand.Reader, c.privateKey, hashAlgo, hashed)
141 | }
142 |
143 | func (c *Certificate) SignBase64(hashAlgo crypto.Hash, content []byte) (string, error) {
144 | signature, err := c.Sign(hashAlgo, content)
145 | if err != nil {
146 | return "", err
147 | }
148 | return base64.StdEncoding.EncodeToString(signature), nil
149 | }
150 |
151 | func (c *Certificate) Verify(hashAlgo crypto.Hash, content, signature []byte) error {
152 | sha := hashAlgo.New()
153 | sha.Write(content)
154 | hashed := sha.Sum(nil)
155 | return rsa.VerifyPKCS1v15(c.PublicKey(), hashAlgo, hashed, signature)
156 | }
157 |
158 | func (c *Certificate) RawTBS() ([]byte, error) {
159 | if c.cert == nil {
160 | return nil, errors.New("certificate is nil")
161 | }
162 | return c.cert.RawTBSCertificate, nil
163 | }
164 |
165 | func (c *Certificate) Raw() ([]byte, error) {
166 | if c.cert == nil {
167 | return nil, errors.New("certificate is nil")
168 | }
169 | return c.cert.Raw, nil
170 | }
171 |
172 | func (c *Certificate) RawBase64() (string, error) {
173 | raw, err := c.Raw()
174 | if err != nil {
175 | return "", err
176 | }
177 | return base64.StdEncoding.EncodeToString(raw), nil
178 | }
179 |
180 | func (c *Certificate) CommonName() string {
181 | if c.cert == nil {
182 | return ""
183 | }
184 | return c.cert.Subject.CommonName
185 | }
186 |
187 | func (c *Certificate) Signature() []byte {
188 | if c.cert == nil {
189 | return nil
190 | }
191 | return c.cert.Signature
192 | }
193 |
194 | func (c *Certificate) PublicKey() *rsa.PublicKey {
195 | if c.privateKey != nil {
196 | return &c.privateKey.PublicKey
197 | }
198 | if c.cert != nil {
199 | return c.cert.PublicKey.(*rsa.PublicKey)
200 | }
201 | return nil
202 | }
203 |
204 | func (c *Certificate) PrivateKey() *rsa.PrivateKey {
205 | if c.privateKey == nil {
206 | return nil
207 | }
208 | return c.privateKey
209 | }
210 |
211 | func GenerateFakeCertificate(parentCommonName, commonName, certPath, keyPath string) (*Certificate, error) {
212 | parentCert, err := GenerateCertificate(parentCommonName, nil)
213 | if err != nil {
214 | return nil, err
215 | }
216 | cert, err := GenerateCertificate(commonName, parentCert)
217 | if err != nil {
218 | return nil, err
219 | }
220 | if certPath != "" {
221 | if err = cert.WriteCertToFile(certPath); err != nil {
222 | return nil, err
223 | }
224 | }
225 | if keyPath != "" {
226 | if err = cert.WritePrivateKeyToFile(keyPath); err != nil {
227 | return nil, err
228 | }
229 | }
230 | return cert, nil
231 | }
232 |
233 | func GenerateCertificate(commonName string, parent *Certificate) (*Certificate, error) {
234 | serialNumber, _ := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
235 | template := &x509.Certificate{
236 | SerialNumber: serialNumber,
237 | Subject: pkix.Name{
238 | CommonName: commonName,
239 | },
240 | NotBefore: carbon.Now().StdTime(),
241 | NotAfter: carbon.Now().AddYears(2).StdTime(),
242 | SignatureAlgorithm: x509.SHA256WithRSA,
243 | }
244 |
245 | var (
246 | parentTemplate *x509.Certificate
247 | parentPrivateKey *rsa.PrivateKey
248 | )
249 |
250 | privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
251 | if err != nil {
252 | return nil, err
253 | }
254 | if parent == nil {
255 | //template.BasicConstraintsValid = true
256 | //template.IsCA = true
257 | parentTemplate = template
258 | parentPrivateKey = privateKey
259 | } else {
260 | parentTemplate = parent.cert
261 | parentPrivateKey = parent.privateKey
262 | }
263 | certBytes, err := x509.CreateCertificate(rand.Reader, template, parentTemplate, &privateKey.PublicKey, parentPrivateKey)
264 | if err != nil {
265 | return nil, err
266 | }
267 | cert, err := x509.ParseCertificate(certBytes)
268 | if err != nil {
269 | return nil, err
270 | }
271 | certificate := &Certificate{
272 | parent: parent,
273 | cert: cert,
274 | privateKey: privateKey,
275 | }
276 | if parent == nil {
277 | certificate.parent = certificate
278 | }
279 | return certificate, nil
280 | }
281 |
282 | const PemPrefix = "-----"
283 |
284 | func MustCreateCertFromFile(certPath, keyPath string) *Certificate {
285 | cert, err := CreateCertFromFile(certPath, keyPath)
286 | if err != nil {
287 | panic(err)
288 | }
289 | return cert
290 | }
291 |
292 | func CreateCertFromFile(certPath, keyPath string) (cert *Certificate, err error) {
293 | if cert, err = CreateCertFromFileWithoutPrivateKey(certPath); err != nil {
294 | return
295 | }
296 | if cert.privateKey, err = CreatePrivateKeyFromFile(keyPath); err != nil {
297 | return nil, err
298 | }
299 | return cert, nil
300 | }
301 |
302 | func CreateCertFromFileWithoutPrivateKey(certPath string) (*Certificate, error) {
303 | certBytes, err := os.ReadFile(certPath)
304 | if err != nil {
305 | return nil, err
306 | }
307 | var cert *Certificate
308 | if strings.HasPrefix(string(certBytes[:len(PemPrefix)]), PemPrefix) {
309 | cert, err = CreateCertFromPem(certBytes)
310 | } else {
311 | certBytes, _ = base64.StdEncoding.DecodeString(string(certBytes))
312 | cert, err = CreateCertFromDer(certBytes)
313 | }
314 | if err != nil {
315 | return nil, err
316 | }
317 | return cert, nil
318 | }
319 |
320 | func CreatePrivateKeyFromFile(path string) (*rsa.PrivateKey, error) {
321 | keyBytes, err := os.ReadFile(path)
322 | if err != nil {
323 | return nil, err
324 | }
325 | var key *rsa.PrivateKey
326 | if strings.HasPrefix(string(keyBytes[:len(PemPrefix)]), PemPrefix) {
327 | key, err = CreatePrivateKeyFromPem(keyBytes)
328 | } else {
329 | key, err = CreatePrivateKeyFromDer(keyBytes)
330 | }
331 | if err != nil {
332 | return nil, err
333 | }
334 | return key, nil
335 | }
336 |
337 | func CreateCertFromPem(bytes []byte) (*Certificate, error) {
338 | block, _ := pem.Decode(bytes)
339 | return CreateCertFromDer(block.Bytes)
340 | }
341 |
342 | func CreatePrivateKeyFromPem(bytes []byte) (*rsa.PrivateKey, error) {
343 | block, _ := pem.Decode(bytes)
344 | return CreatePrivateKeyFromDer(block.Bytes)
345 | }
346 |
347 | func CreateCertFromDer(bytes []byte) (*Certificate, error) {
348 | cert, err := x509.ParseCertificate(bytes)
349 | if err != nil {
350 | return nil, err
351 | }
352 | return &Certificate{
353 | cert: cert,
354 | }, nil
355 | }
356 |
357 | func CreatePrivateKeyFromDer(bytes []byte) (*rsa.PrivateKey, error) {
358 | key, err := x509.ParsePKCS1PrivateKey(bytes)
359 | if err != nil {
360 | return nil, err
361 | }
362 | return key, nil
363 | }
364 |
--------------------------------------------------------------------------------
/internal/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "fmt"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/algo"
6 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
7 | "math/big"
8 | "strings"
9 | )
10 |
11 | func BuildPowerConfig(certs ...[2]*cert.Certificate) string {
12 | lines := make([]string, 0, 4)
13 | lines = append(lines, "[Result]")
14 | for _, certPair := range certs {
15 | var (
16 | x, z, r string
17 | y int
18 | subCert = certPair[0]
19 | realParentCert = certPair[1]
20 | )
21 | bi := new(big.Int)
22 | bi.SetBytes(subCert.Signature())
23 | x = bi.String()
24 | y = subCert.PublicKey().E
25 | z = realParentCert.PublicKey().N.String()
26 | bi = new(big.Int)
27 | subCertRawTBS, _ := subCert.RawTBS()
28 | em, err := algo.GetEM(cert.JetProfileCert.PublicKey(), subCertRawTBS)
29 | if err != nil {
30 | continue
31 | }
32 | bi.SetBytes(em)
33 | r = bi.String()
34 | lines = append(lines, fmt.Sprintf("EQUAL,%s,%d,%s->%s", x, y, z, r))
35 | }
36 | return strings.Join(lines, "\n")
37 | }
38 |
39 | func BuildDnsConfig() string {
40 | builder := new(strings.Builder)
41 | builder.WriteString("[DNS]\n")
42 | builder.WriteString("EQUAL,jetbrains.com\n")
43 | builder.WriteString("EQUAL,plugin.obroom.com")
44 | return builder.String()
45 | }
46 |
47 | func BuildUrlConfig() string {
48 | builder := new(strings.Builder)
49 | builder.WriteString("[URL]\n")
50 | builder.WriteString("PREFIX,https://account.jetbrains.com/lservice/rpc/validateKey.action\n")
51 | builder.WriteString("PREFIX,https://account.jetbrains.com.cn/lservice/rpc/validateKey.action")
52 | return builder.String()
53 | }
54 |
--------------------------------------------------------------------------------
/internal/license/code.go:
--------------------------------------------------------------------------------
1 | package license
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "errors"
7 | "fmt"
8 | "github.com/lovesasuna/sync/coroutinegroup"
9 | "net/http"
10 | )
11 |
12 | const (
13 | DataBaseUrl = "https://data.services.jetbrains.com"
14 | PluginBaseUrl = "https://plugins.jetbrains.com"
15 | )
16 |
17 | type ProductDto struct {
18 | Code string `json:"code"`
19 | SalesCode string `json:"salesCode"`
20 | Name string `json:"name"`
21 | Description string `json:"description"`
22 | ForSale bool `json:"forSale"`
23 | }
24 |
25 | func GetProductCode() (codes []string, err error) {
26 | resp, err := http.Get(fmt.Sprintf("%s/products?fields=name,code,forSale,salesCode,description", DataBaseUrl))
27 | if err != nil {
28 | return nil, err
29 | }
30 | defer resp.Body.Close()
31 | var productList []ProductDto
32 | if err = json.NewDecoder(resp.Body).Decode(&productList); err != nil {
33 | return nil, err
34 | }
35 | for _, product := range productList {
36 | codes = append(codes, product.Code)
37 | if product.ForSale && product.SalesCode != "" {
38 | codes = append(codes, product.SalesCode)
39 | }
40 | }
41 | return
42 | }
43 |
44 | type PluginDto struct {
45 | ID int32 `json:"id"`
46 | XMLID string `json:"xmlId"`
47 | Link string `json:"link"`
48 | Name string `json:"name"`
49 | Preview string `json:"preview"`
50 | Downloads int `json:"downloads"`
51 | PricingModel string `json:"pricingModel"`
52 | Icon string `json:"icon"`
53 | PreviewImage string `json:"previewImage"`
54 | Cdate int64 `json:"cdate"`
55 | Rating float64 `json:"rating"`
56 | HasSource bool `json:"hasSource"`
57 | Tags []string `json:"tags"`
58 | Vendor Vendor `json:"vendor"`
59 | }
60 |
61 | type PluginDetail struct {
62 | ID int32 `json:"id"`
63 | Name string `json:"name"`
64 | Link string `json:"link"`
65 | Approve bool `json:"approve"`
66 | XMLID string `json:"xmlId"`
67 | Description string `json:"description"`
68 | CustomIdeList bool `json:"customIdeList"`
69 | Preview string `json:"preview"`
70 | DocText string `json:"docText"`
71 | Cdate int64 `json:"cdate"`
72 | Family string `json:"family"`
73 | Downloads int `json:"downloads"`
74 | PurchaseInfo PurchaseInfo `json:"purchaseInfo"`
75 | Vendor Vendor `json:"vendor"`
76 | Urls Urls `json:"urls"`
77 | Tags []Tags `json:"tags"`
78 | HasUnapprovedUpdate bool `json:"hasUnapprovedUpdate"`
79 | PricingModel string `json:"pricingModel"`
80 | Screens []Screens `json:"screens"`
81 | Icon string `json:"icon"`
82 | IsHidden bool `json:"isHidden"`
83 | IsMonetizationAvailable bool `json:"isMonetizationAvailable"`
84 | IsBlocked bool `json:"isBlocked"`
85 | IsModificationAllowed bool `json:"isModificationAllowed"`
86 | }
87 |
88 | type PurchaseInfo struct {
89 | ProductCode string `json:"productCode"`
90 | BuyURL interface{} `json:"buyUrl"`
91 | PurchaseTerms interface{} `json:"purchaseTerms"`
92 | Optional bool `json:"optional"`
93 | TrialPeriod int `json:"trialPeriod"`
94 | }
95 |
96 | type Details struct {
97 | City string `json:"city"`
98 | Address string `json:"address"`
99 | State interface{} `json:"state"`
100 | Zip string `json:"zip"`
101 | Phone string `json:"phone"`
102 | }
103 |
104 | type Vendor struct {
105 | Type string `json:"type"`
106 | ID int `json:"id"`
107 | Name string `json:"name"`
108 | URL string `json:"url"`
109 | Link string `json:"link"`
110 | PublicName string `json:"publicName"`
111 | Email string `json:"email"`
112 | CountryCode string `json:"countryCode"`
113 | Country string `json:"country"`
114 | IsVerified bool `json:"isVerified"`
115 | VendorID int `json:"vendorId"`
116 | Details Details `json:"details"`
117 | IsTrader bool `json:"isTrader"`
118 | }
119 |
120 | type Urls struct {
121 | URL string `json:"url"`
122 | ForumURL string `json:"forumUrl"`
123 | LicenseURL string `json:"licenseUrl"`
124 | PrivacyPolicyURL string `json:"privacyPolicyUrl"`
125 | BugtrackerURL string `json:"bugtrackerUrl"`
126 | DocURL string `json:"docUrl"`
127 | SourceCodeURL string `json:"sourceCodeUrl"`
128 | }
129 |
130 | type Tags struct {
131 | ID int `json:"id"`
132 | Name string `json:"name"`
133 | Privileged bool `json:"privileged"`
134 | Searchable bool `json:"searchable"`
135 | Link string `json:"link"`
136 | }
137 |
138 | type Screens struct {
139 | URL string `json:"url"`
140 | }
141 |
142 | func GetPluginCode(max, offset int32, keyword string) (codes []string, err error) {
143 | resp, err := http.Get(fmt.Sprintf("%s/api/searchPlugins?max=%d&offset=%d&search=%s", PluginBaseUrl, max, offset, keyword))
144 | if err != nil {
145 | return nil, err
146 | }
147 | defer resp.Body.Close()
148 |
149 | var pluginListResp struct {
150 | Plugins []*PluginDto `json:"plugins"`
151 | }
152 | err = json.NewDecoder(resp.Body).Decode(&pluginListResp)
153 | if err != nil {
154 | return nil, err
155 | }
156 |
157 | pluginDetailChan := make(chan *PluginDetail, len(pluginListResp.Plugins))
158 | group, _ := coroutinegroup.WithContext(context.Background())
159 | group.SetMaxErrorTask(int32(len(pluginListResp.Plugins) / 3))
160 | group.SetGlobalRetryTimes(1)
161 | for _, plugin := range pluginListResp.Plugins {
162 | if plugin.PricingModel == "FREE" {
163 | continue
164 | }
165 | if plugin.Icon != "" {
166 | plugin.Icon = PluginBaseUrl + plugin.Icon
167 | }
168 | p := plugin
169 | group.Go(
170 | func(ctx context.Context) error {
171 | detail, err := getDetailByPluginId(p.ID)
172 | if err != nil {
173 | return err
174 | }
175 | pluginDetailChan <- detail
176 | return nil
177 | },
178 | )
179 | }
180 | errs := group.Wait()
181 | close(pluginDetailChan)
182 | if len(errs) > 0 {
183 | return nil, errors.Join(errs...)
184 | }
185 | for detail := range pluginDetailChan {
186 | code := detail.PurchaseInfo.ProductCode
187 | if code != "" {
188 | codes = append(codes, code)
189 | }
190 | }
191 | return
192 | }
193 |
194 | func getDetailByPluginId(id int32) (detail *PluginDetail, err error) {
195 | resp, err := http.Get(fmt.Sprintf("%s/api/plugins/%d", PluginBaseUrl, id))
196 | if err != nil {
197 | return nil, err
198 | }
199 | defer resp.Body.Close()
200 |
201 | detail = new(PluginDetail)
202 | err = json.NewDecoder(resp.Body).Decode(detail)
203 | return
204 | }
205 |
--------------------------------------------------------------------------------
/internal/license/license.go:
--------------------------------------------------------------------------------
1 | package license
2 |
3 | import (
4 | "crypto"
5 | "encoding/base64"
6 | "encoding/json"
7 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
8 | "github.com/dromara/carbon/v2"
9 | "strings"
10 | )
11 |
12 | type License struct {
13 | LicenseID string `json:"licenseId"`
14 | LicenseeName string `json:"licenseeName"`
15 | AssigneeName string `json:"assigneeName"`
16 | AssigneeEmail string `json:"assigneeEmail"`
17 | LicenseRestriction string `json:"licenseRestriction"`
18 | CheckConcurrentUse bool `json:"checkConcurrentUse"`
19 | Products []Product `json:"products"`
20 | Metadata string `json:"metadata"`
21 | Hash string `json:"hash"`
22 | GracePeriodDays int `json:"gracePeriodDays"`
23 | AutoProlongated bool `json:"autoProlongated"`
24 | IsAutoProlongated bool `json:"isAutoProlongated"`
25 | }
26 |
27 | type Product struct {
28 | Code string `json:"code"`
29 | FallbackDate string `json:"fallbackDate"`
30 | PaidUpTo string `json:"paidUpTo"`
31 | }
32 |
33 | func GenerateLicenseCode(cert *cert.Certificate, licenseId, licenseeName, assigneeName, assigneeEmail, time string, codes ...string) (string, error) {
34 | license, err := GenerateLicense(
35 | licenseId,
36 | licenseeName,
37 | assigneeName,
38 | assigneeEmail,
39 | time,
40 | codes...,
41 | )
42 | if err != nil {
43 | return "", err
44 | }
45 | licenseJs, _ := json.Marshal(license)
46 | licensePartBase64 := base64.StdEncoding.EncodeToString(licenseJs)
47 |
48 | certPartBase64, _ := cert.RawBase64()
49 |
50 | signatureBytes, _ := cert.Sign(crypto.SHA1, licenseJs)
51 | signatureBase64 := base64.StdEncoding.EncodeToString(signatureBytes)
52 |
53 | return strings.Join([]string{licenseId, licensePartBase64, signatureBase64, certPartBase64}, "-"), nil
54 | }
55 |
56 | func GenerateLicense(licenseId, licenseeName, assigneeName, assigneeEmail, time string, codes ...string) (*License, error) {
57 | fallBackDate := carbon.Now().SetLayout(carbon.DateLayout).String()
58 | paidUpTo := carbon.ParseByLayout(time, carbon.DateLayout).String()
59 |
60 | products := make([]Product, 0, len(codes))
61 | for _, code := range codes {
62 | products = append(products, Product{
63 | Code: code,
64 | FallbackDate: fallBackDate,
65 | PaidUpTo: paidUpTo,
66 | })
67 | }
68 |
69 | license := &License{
70 | LicenseID: licenseId,
71 | LicenseeName: licenseeName,
72 | AssigneeName: assigneeName,
73 | AssigneeEmail: assigneeEmail,
74 | Products: products,
75 | Metadata: "0120230102PPAA013009",
76 | Hash: "41472961/0:1563609451",
77 | GracePeriodDays: 7,
78 | AutoProlongated: true,
79 | IsAutoProlongated: true,
80 | }
81 | return license, nil
82 | }
83 |
--------------------------------------------------------------------------------
/internal/util/random.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "math/rand"
4 |
5 | const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
6 |
7 | func GetRandomString(length int) string {
8 | if length <= 0 {
9 | return ""
10 | }
11 | bytes := make([]byte, length)
12 | for i := 0; i < length; i++ {
13 | bytes[i] = letters[rand.Intn(len(letters))]
14 | }
15 | return string(bytes)
16 | }
17 |
--------------------------------------------------------------------------------
/justfile:
--------------------------------------------------------------------------------
1 | default: backend frontend caddy
2 | caddy run --config ./Caddyfile
3 |
4 | caddy:
5 | mkdir -p /var/log/caddy
6 | wget -q -O /usr/local/bin/caddy "https://caddyserver.com/api/download?os=linux&arch=amd64" && chmod +x /usr/local/bin/caddy
7 |
8 | frontend:
9 | just frontend/
10 |
11 | backend: build_backend
12 | ./jetbrains_hacker run-server --addr :8080 &
13 |
14 | build_backend:
15 | go mod tidy
16 | go build -v -o jetbrains_hacker
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/cmd"
5 | )
6 |
7 | func main() {
8 | cmd.Execute()
9 | }
10 |
--------------------------------------------------------------------------------
/server/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | var Config *ServerConfig
4 |
5 | type ServerConfig struct {
6 | Addr string
7 | Licensee string
8 | UserCertPath string
9 | UserPrivateKeyPath string
10 | LicenseServerCertPath string
11 | LicenseServerPrivateKeyPath string
12 | }
13 |
14 | func InitServerConfig(c *ServerConfig) {
15 | Config = c
16 | }
17 |
--------------------------------------------------------------------------------
/server/handler/base.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "crypto"
5 | "encoding/xml"
6 | "fmt"
7 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
8 | "net/http"
9 | "strings"
10 | "time"
11 | )
12 |
13 | type BaseRequest struct {
14 | Salt string `form:"salt"`
15 | UserName string `form:"userName"`
16 | MachineId string `form:"machineId"`
17 | }
18 |
19 | type Helper struct {
20 | *CertPool
21 | }
22 |
23 | func NewHelper(pool *CertPool) *Helper {
24 | response := &Helper{
25 | CertPool: pool,
26 | }
27 | return response
28 | }
29 |
30 | func (b *Helper) GenerateConfirmationStamp(machineId string) string {
31 | timeStamp := time.Now().UnixMilli()
32 | licenseStr := fmt.Sprintf("%d:%s", timeStamp, machineId)
33 | licenseServerCert := b.LicenseServerCert
34 | signatureBase64, _ := licenseServerCert.SignBase64(crypto.SHA1, []byte(licenseStr))
35 | rawUserCertBase64, _ := licenseServerCert.RawBase64()
36 | return fmt.Sprintf("%s:SHA1withRSA:%s:%s", licenseStr, signatureBase64, rawUserCertBase64)
37 | }
38 |
39 | func (b *Helper) GenerateLeaseSignature(serverLease string) string {
40 | leaseSignature, _ := b.UserCert.SignBase64(crypto.SHA512, []byte(serverLease))
41 | rawUserCertBase64, _ := b.UserCert.RawBase64()
42 | return fmt.Sprintf("SHA512withRSA-%s-%s", leaseSignature, rawUserCertBase64)
43 | }
44 |
45 | func (b *Helper) GetServerUid() string {
46 | serverUid := "custom"
47 | licenseServerCommonName := b.LicenseServerCert.CommonName()
48 | if strings.Contains(licenseServerCommonName, ".") {
49 | serverUid = strings.Split(licenseServerCommonName, ".")[0]
50 | }
51 | return serverUid
52 | }
53 |
54 | func (b *Helper) Sign(content []byte) (result []byte, err error) {
55 | signAlgo := crypto.SHA1
56 | signature, err := b.LicenseServerCert.SignBase64(signAlgo, content)
57 | if err != nil {
58 | return nil, err
59 | }
60 | rawLicenseServerCertBase64, _ := b.LicenseServerCert.RawBase64()
61 | return []byte(fmt.Sprintf("", signature, rawLicenseServerCertBase64)), nil
62 | }
63 |
64 | type Signable interface {
65 | Sign(content []byte) (result []byte, err error)
66 | }
67 |
68 | type SignedResponse struct {
69 | Signable
70 | }
71 |
72 | func (t *SignedResponse) Render(w http.ResponseWriter) error {
73 | t.WriteContentType(w)
74 | content, err := xml.Marshal(t.Signable)
75 | if err != nil {
76 | return err
77 | }
78 | signature, err := t.Signable.Sign(content)
79 | if err != nil {
80 | return err
81 | }
82 | _, err = w.Write(signature)
83 | if err != nil {
84 | return err
85 | }
86 | _, _ = w.Write([]byte("\n"))
87 | _, err = w.Write(content)
88 | return err
89 | }
90 |
91 | func (t *SignedResponse) WriteContentType(w http.ResponseWriter) {
92 | w.Header()["Content-Type"] = []string{"text/xml; charset=utf-8"}
93 | }
94 |
95 | const CertPoolKey = "cert_pool"
96 |
97 | type CertPool struct {
98 | LicenseServerCert *cert.Certificate
99 | UserCert *cert.Certificate
100 | }
101 |
--------------------------------------------------------------------------------
/server/handler/config.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/config"
6 | "github.com/gin-gonic/gin"
7 | "net/http"
8 | )
9 |
10 | func Config(context *gin.Context) {
11 | var (
12 | configText string
13 | _type = context.Param("type")
14 | )
15 | switch _type {
16 | case "dns":
17 | configText = config.BuildDnsConfig()
18 | case "url":
19 | configText = config.BuildUrlConfig()
20 | default:
21 | _type = "power"
22 | pool := context.Value(CertPoolKey).(*CertPool)
23 | configText = config.BuildPowerConfig(
24 | [2]*cert.Certificate{
25 | pool.UserCert, cert.JetProfileCert,
26 | },
27 | [2]*cert.Certificate{
28 | pool.LicenseServerCert, cert.LicenseServerCert,
29 | },
30 | )
31 | }
32 | context.JSON(
33 | http.StatusOK,
34 | struct {
35 | Type string `json:"type"`
36 | Config string `json:"config"`
37 | }{
38 | Type: _type,
39 | Config: configText,
40 | },
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/server/handler/license.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/internal/license"
5 | "github.com/gin-gonic/gin"
6 | "net/http"
7 | "strings"
8 | )
9 |
10 | func License(context *gin.Context) {
11 | pool := context.Value(CertPoolKey).(*CertPool)
12 | type Param struct {
13 | LicenseId string `form:"licenseId"`
14 | Name string `form:"name"`
15 | User string `form:"user"`
16 | Email string `form:"email"`
17 | Time string `form:"time"`
18 | Codes string `form:"codes"`
19 | }
20 | param := new(Param)
21 | err := context.Bind(param)
22 | if err != nil {
23 | _ = context.AbortWithError(http.StatusInternalServerError, err)
24 | return
25 | }
26 | licenseCode, err := license.GenerateLicenseCode(
27 | pool.UserCert,
28 | param.LicenseId,
29 | param.Name,
30 | param.User,
31 | param.Email,
32 | param.Time,
33 | strings.Split(param.Codes, ",")...,
34 | )
35 | if err != nil {
36 | _ = context.AbortWithError(http.StatusInternalServerError, err)
37 | return
38 | }
39 | context.JSON(
40 | http.StatusOK,
41 | struct {
42 | LicenseCode string `json:"licenseCode"`
43 | }{
44 | LicenseCode: licenseCode,
45 | },
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/server/handler/obtain_ticket.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/util"
6 | "github.com/LovesAsuna/jetbrains_hacker/server/config"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | )
10 |
11 | func ObtainTicket(context *gin.Context) {
12 | response, err := NewObtainTicketResponse(context)
13 | if err != nil {
14 | _ = context.AbortWithError(http.StatusInternalServerError, err)
15 | return
16 | }
17 | context.Render(http.StatusOK, &SignedResponse{response})
18 | }
19 |
20 | type ObtainTicketResponse struct {
21 | *Helper `xml:"-"`
22 | Action string `xml:"action"`
23 | ConfirmationStamp string `xml:"confirmationStamp"`
24 | LeaseSignature string `xml:"leaseSignature"`
25 | Message string `xml:"message"`
26 | ProlongationPeriod int `xml:"prolongationPeriod,omitempty"`
27 | ResponseCode string `xml:"responseCode"`
28 | Salt string `xml:"salt"`
29 | ServerLease string `xml:"serverLease"`
30 | ServerUid string `xml:"serverUid"`
31 | TicketID string `xml:"ticketId"`
32 | TicketProperties string `xml:"ticketProperties"`
33 | ValidationDeadline int `xml:"validationDeadlinePeriod"`
34 | ValidationPeriod int `xml:"validationPeriod"`
35 | }
36 |
37 | func NewObtainTicketResponse(context *gin.Context) (*ObtainTicketResponse, error) {
38 | baseRequest := new(BaseRequest)
39 | if err := context.Bind(baseRequest); err != nil {
40 | return nil, err
41 | }
42 | helper := NewHelper(context.Value(CertPoolKey).(*CertPool))
43 | serverUid := helper.GetServerUid()
44 | serverLease := "4102415999000:" + serverUid
45 | licensee := config.Config.Licensee
46 | if licensee == "" {
47 | licensee = baseRequest.UserName
48 | }
49 | return &ObtainTicketResponse{
50 | Helper: helper,
51 | Action: "NONE",
52 | ConfirmationStamp: helper.GenerateConfirmationStamp(baseRequest.MachineId),
53 | LeaseSignature: helper.GenerateLeaseSignature(serverLease),
54 | Message: "",
55 | ProlongationPeriod: 600000,
56 | ResponseCode: "OK",
57 | Salt: baseRequest.Salt,
58 | ServerLease: serverLease,
59 | ServerUid: serverUid,
60 | TicketID: util.GetRandomString(10),
61 | TicketProperties: fmt.Sprintf("licensee=%s", licensee),
62 | ValidationDeadline: -1,
63 | ValidationPeriod: 60000000,
64 | }, nil
65 | }
66 |
--------------------------------------------------------------------------------
/server/handler/ping.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | func Ping(context *gin.Context) {
9 | response, err := NewPingResponse(context)
10 | if err != nil {
11 | _ = context.AbortWithError(http.StatusInternalServerError, err)
12 | return
13 | }
14 | context.Render(http.StatusOK, &SignedResponse{response})
15 | }
16 |
17 | type PingResponse struct {
18 | *Helper `xml:"-"`
19 | Action string `xml:"action"`
20 | ConfirmationStamp string `xml:"confirmationStamp"`
21 | LeaseSignature string `xml:"leaseSignature"`
22 | Message string `xml:"message"`
23 | ResponseCode string `xml:"responseCode"`
24 | Salt string `xml:"salt"`
25 | ServerLease string `xml:"serverLease"`
26 | ServerUid string `xml:"serverUid"`
27 | ValidationDeadline int `xml:"validationDeadlinePeriod"`
28 | ValidationPeriod int `xml:"validationPeriod"`
29 | }
30 |
31 | func NewPingResponse(context *gin.Context) (*PingResponse, error) {
32 | baseRequest := new(BaseRequest)
33 | if err := context.Bind(baseRequest); err != nil {
34 | return nil, err
35 | }
36 | helper := NewHelper(context.Value(CertPoolKey).(*CertPool))
37 | serverUid := helper.GetServerUid()
38 | serverLease := "4102415999000:" + serverUid
39 | return &PingResponse{
40 | Helper: helper,
41 | Action: "NONE",
42 | ConfirmationStamp: helper.GenerateConfirmationStamp(baseRequest.MachineId),
43 | LeaseSignature: helper.GenerateLeaseSignature(serverLease),
44 | Message: "",
45 | ResponseCode: "OK",
46 | Salt: baseRequest.Salt,
47 | ServerLease: serverLease,
48 | ServerUid: serverUid,
49 | ValidationDeadline: -1,
50 | ValidationPeriod: 60000000,
51 | }, nil
52 | }
53 |
--------------------------------------------------------------------------------
/server/handler/release_ticket.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | func ReleaseTicket(context *gin.Context) {
9 | response, err := NewReleaseTicketResponse(context)
10 | if err != nil {
11 | _ = context.AbortWithError(http.StatusInternalServerError, err)
12 | return
13 | }
14 | context.Render(http.StatusOK, &SignedResponse{response})
15 | }
16 |
17 | type ReleaseTicketResponse struct {
18 | *Helper `xml:"-"`
19 | Action string `xml:"action"`
20 | ConfirmationStamp string `xml:"confirmationStamp"`
21 | LeaseSignature string `xml:"leaseSignature"`
22 | Message string `xml:"message"`
23 | ResponseCode string `xml:"responseCode"`
24 | Salt string `xml:"salt"`
25 | ServerLease string `xml:"serverLease"`
26 | ServerUid string `xml:"serverUid"`
27 | ValidationDeadline int `xml:"validationDeadlinePeriod"`
28 | ValidationPeriod int `xml:"validationPeriod"`
29 | }
30 |
31 | func NewReleaseTicketResponse(context *gin.Context) (*ReleaseTicketResponse, error) {
32 | baseRequest := new(BaseRequest)
33 | if err := context.Bind(baseRequest); err != nil {
34 | return nil, err
35 | }
36 | helper := NewHelper(context.Value(CertPoolKey).(*CertPool))
37 | serverUid := helper.GetServerUid()
38 | serverLease := "4102415999000:" + serverUid
39 |
40 | return &ReleaseTicketResponse{
41 | Helper: nil,
42 | Action: "NONE",
43 | ConfirmationStamp: helper.GenerateConfirmationStamp(baseRequest.MachineId),
44 | LeaseSignature: helper.GenerateLeaseSignature(serverLease),
45 | Message: "",
46 | ResponseCode: "OK",
47 | Salt: baseRequest.Salt,
48 | ServerLease: serverLease,
49 | ServerUid: serverUid,
50 | ValidationDeadline: -1,
51 | ValidationPeriod: 600000,
52 | }, nil
53 | }
54 |
--------------------------------------------------------------------------------
/server/middleware/certificate.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "fmt"
5 | "github.com/LovesAsuna/jetbrains_hacker/internal/cert"
6 | "github.com/LovesAsuna/jetbrains_hacker/server/config"
7 | "github.com/LovesAsuna/jetbrains_hacker/server/handler"
8 | "github.com/gin-gonic/gin"
9 | "net/http"
10 | "os"
11 | "sync"
12 | )
13 |
14 | var certCache = struct {
15 | userCert *cert.Certificate
16 | licenseServerCert *cert.Certificate
17 | rwLock sync.RWMutex
18 | }{}
19 |
20 | func InjectCertificate(context *gin.Context) {
21 | certCache.rwLock.RLock()
22 | if certCache.userCert == nil || certCache.licenseServerCert == nil {
23 | certCache.rwLock.RUnlock()
24 | certCache.rwLock.Lock()
25 | if certCache.userCert != nil && certCache.licenseServerCert != nil {
26 | certCache.rwLock.Unlock()
27 | } else {
28 | var err error
29 | isExist := func(filePath string) bool {
30 | _, err := os.Stat(filePath)
31 | return err == nil || !os.IsNotExist(err)
32 | }
33 | if isExist(config.Config.UserCertPath) && isExist(config.Config.UserPrivateKeyPath) {
34 | certCache.userCert, err = cert.CreateCertFromFile(config.Config.UserCertPath, config.Config.UserPrivateKeyPath)
35 | if err != nil {
36 | certCache.rwLock.Lock()
37 | _ = context.AbortWithError(http.StatusInternalServerError, err)
38 | return
39 | }
40 | } else {
41 | if certCache.userCert, err = cert.GenerateFakeCertificate(
42 | cert.JetProfileCert.CommonName(),
43 | "create by license server",
44 | config.Config.UserCertPath,
45 | config.Config.UserPrivateKeyPath,
46 | ); err != nil {
47 | certCache.rwLock.Lock()
48 | _ = context.AbortWithError(http.StatusInternalServerError, err)
49 | return
50 | }
51 | }
52 |
53 | if isExist(config.Config.LicenseServerCertPath) && isExist(config.Config.LicenseServerPrivateKeyPath) {
54 | certCache.licenseServerCert, err = cert.CreateCertFromFile(config.Config.LicenseServerCertPath, config.Config.LicenseServerPrivateKeyPath)
55 | if err != nil {
56 | certCache.rwLock.Lock()
57 | _ = context.AbortWithError(http.StatusInternalServerError, err)
58 | return
59 | }
60 | } else {
61 | if certCache.licenseServerCert, err = cert.GenerateFakeCertificate(
62 | cert.LicenseServerCert.CommonName(),
63 | fmt.Sprintf("%s.lsrv.jetbrains.com", "license_server"),
64 | config.Config.LicenseServerCertPath,
65 | config.Config.LicenseServerPrivateKeyPath,
66 | ); err != nil {
67 | certCache.rwLock.Lock()
68 | _ = context.AbortWithError(http.StatusInternalServerError, err)
69 | return
70 | }
71 | }
72 | certCache.rwLock.Unlock()
73 | }
74 | } else {
75 | certCache.rwLock.RUnlock()
76 | }
77 |
78 | certPool := &handler.CertPool{
79 | UserCert: certCache.userCert,
80 | LicenseServerCert: certCache.licenseServerCert,
81 | }
82 | context.Set(handler.CertPoolKey, certPool)
83 | }
84 |
--------------------------------------------------------------------------------
/server/middleware/middleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import "github.com/gin-gonic/gin"
4 |
5 | func SetMiddleWare(engine *gin.Engine) {
6 | engine.Use(InjectCertificate)
7 | }
8 |
--------------------------------------------------------------------------------
/server/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/server/handler"
5 | "github.com/gin-gonic/gin"
6 | )
7 |
8 | func SetRouter(engine *gin.Engine) {
9 | engine.Handle("GET", "/rpc/obtainTicket.action", handler.ObtainTicket)
10 | engine.Handle("GET", "/rpc/ping.action", handler.Ping)
11 | engine.Handle("GET", "/rpc/releaseTicket.action", handler.ReleaseTicket)
12 | engine.Handle("GET", "/rpc/license", handler.License)
13 | engine.Handle("GET", "/config/:type", handler.Config)
14 | }
15 |
--------------------------------------------------------------------------------
/server/server.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/LovesAsuna/jetbrains_hacker/server/config"
5 | "github.com/LovesAsuna/jetbrains_hacker/server/middleware"
6 | "github.com/LovesAsuna/jetbrains_hacker/server/router"
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | func RunServer() error {
11 | gin.SetMode(gin.ReleaseMode)
12 | engine := gin.Default()
13 | middleware.SetMiddleWare(engine)
14 | router.SetRouter(engine)
15 | return engine.Run(config.Config.Addr)
16 | }
17 |
--------------------------------------------------------------------------------