Add source

This commit is contained in:
0x1eef 2023-09-26 21:57:29 -03:00
parent ff1b6186cb
commit 5767e222cb
22 changed files with 425 additions and 1 deletions

7
.editorconfig Normal file
View file

@ -0,0 +1,7 @@
[*]
end_of_line = lf
insert_final_newline = true
[{*.js,*.ts,*.tsx,*.json,*.css}]
indent_style = space
indent_size = 2

28
.eslintrc.js Normal file
View file

@ -0,0 +1,28 @@
module.exports = {
extends: ["standard-with-typescript", "standard-jsx", "prettier"],
parserOptions: {
project: "./tsconfig.json",
},
rules: {
"@typescript-eslint/member-delimiter-style": 2,
"@typescript-eslint/semi": ["error", "always"],
"@typescript-eslint/no-extra-semi": "error",
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/strict-boolean-expressions": 0,
"@typescript-eslint/no-floating-promises": 0,
"@typescript-eslint/prefer-nullish-coalescing": 0,
"@typescript-eslint/restrict-template-expressions": 0,
"@typescript-eslint/promise-function-async": 0,
"@typescript-eslint/consistent-type-definitions": 0,
"@typescript-eslint/no-misused-promises": ["error", {"checksConditionals": false}],
"@typescript-eslint/no-redeclare": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/member-delimiter-style": 0,
"import/no-absolute-path": 0,
"no-return-assign": 0,
"no-useless-return": 0,
"quotes": 0,
"object-curly-spacing": 2,
"n/no-callback-literal": 0,
},
};

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build/
node_modules/

2
.projectile Normal file
View file

@ -0,0 +1,2 @@
-node_modules/
-build/

46
README.md Normal file
View file

@ -0,0 +1,46 @@
## About
Photon is a Google Chrome extension that provides information about
the public IP address you are using to connect to the internet. When
the extension's icon is clicked, the extension makes a request to the
[clean.wtfismyip.com](https://clean.wtfismyip.com)
web service to discover information about your public IP address, and
then displays that information in a simple HTML view.
## Features
* Displays a public IP address.
* Displays the Internet Service Provider (ISP) associated with a public IP address.
* Displays the city associated with a public IP address.
* Displays the country associated with a public IP address.
* Displays whether or not a public IP address is a Tor exit node.
## Install
Produce the "build" directory:
git clone https://github.com/0x1eef/photon.git
cd photon
npm i
npm run build
Add the extension to Chrome:
* Visit `chrome://extensions`.
* Check "Developer mode" (top right hand corner).
* Click "Load unpacked extension".
* Choose the "build/" directory from the file dialog.
* Done.
## Sources
* [Source code (GitHub)](https://github.com/0x1eef/photon#readme)
* [Source code (GitLab)](https://gitlab.com/0x1eef/photon#about)
## <a id="license"> License </a>
[BSD Zero Clause](https://choosealicense.com/licenses/0bsd/).
<br>
See [LICENSE](./LICENSE)

32
package-lock.json generated
View file

@ -10,6 +10,7 @@
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/chrome": "^0.0.246",
"@types/react": "^18.0.18", "@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"clean-webpack-plugin": "^4.0.0", "clean-webpack-plugin": "^4.0.0",
@ -224,6 +225,16 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@types/chrome": {
"version": "0.0.246",
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.246.tgz",
"integrity": "sha512-MxGxEomGxsJiL9xe/7ZwVgwdn8XVKWbPvxpVQl3nWOjrS0Ce63JsfzxUc4aU3GvRcUPYsfufHmJ17BFyKxeA4g==",
"dev": true,
"dependencies": {
"@types/filesystem": "*",
"@types/har-format": "*"
}
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.44.3", "version": "8.44.3",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.3.tgz",
@ -250,6 +261,21 @@
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==", "integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
"dev": true "dev": true
}, },
"node_modules/@types/filesystem": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.33.tgz",
"integrity": "sha512-2KedRPzwu2K528vFkoXnnWdsG0MtUwPjuA7pRy4vKxlxHEe8qUDZibYHXJKZZr2Cl/ELdCWYqyb/MKwsUuzBWw==",
"dev": true,
"dependencies": {
"@types/filewriter": "*"
}
},
"node_modules/@types/filewriter": {
"version": "0.0.30",
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.30.tgz",
"integrity": "sha512-lB98tui0uxc7erbj0serZfJlHKLNJHwBltPnbmO1WRpL5T325GOHRiQfr2E29V2q+S1brDO63Fpdt6vb3bES9Q==",
"dev": true
},
"node_modules/@types/glob": { "node_modules/@types/glob": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@ -260,6 +286,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/har-format": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.13.tgz",
"integrity": "sha512-PwBsCBD3lDODn4xpje3Y1di0aDJp4Ww7aSfMRVw6ysnxD4I7Wmq2mBkSKaDtN403hqH5sp6c9xQUvFYY3+lkBg==",
"dev": true
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.13", "version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",

View file

@ -1,10 +1,12 @@
{ {
"name": "whatismyip", "name": "photon",
"version": "0.1.0",
"dependencies": { "dependencies": {
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@types/chrome": "^0.0.246",
"@types/react": "^18.0.18", "@types/react": "^18.0.18",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.0.6",
"clean-webpack-plugin": "^4.0.0", "clean-webpack-plugin": "^4.0.0",

View file

@ -0,0 +1,26 @@
{
"ip_address": {
"message": "IP Address"
},
"isp": {
"message": "ISP"
},
"city": {
"message": "City"
},
"country": {
"message": "Country"
},
"tor_exit_node": {
"message": "Tor exit node"
},
"loading": {
"message": "Loading..."
},
"yes": {
"message": "Yes"
},
"no": {
"message": "No"
}
}

View file

@ -0,0 +1,10 @@
body .response {
padding: 30px;
}
body .response .row {
display: flex;
place-content: space-between;
font-size: var(--default-font-size);
margin: 0 0 10px 0;
}

52
src/css/index.css Normal file
View file

@ -0,0 +1,52 @@
:root {
/* Defaults */
--default-font-size: 1.15em;
--default-font-family: "SF Mono","Segoe UI Mono","Roboto Mono",Menlo,Courier,monospace;
/* Colors */
--primary-red: #d73e48;
--primary-green: #32b643;
--primary-blue: #302ecd;
--secondary-blue: #807fe2;
--primary-white: #FFF;
--secondary-white: #f1f1fc;
}
body {
font-family: var(--default-font-family);
width: 368px;
height: 212px;
margin: 0;
}
#root, .loader {
height: 100%;
}
a {
color: var(--primary-blue) !important;
text-decoration: none;
}
a:visited {
color: var(--primary-blue) !important;
}
a:active, a:focus, a:hover {
color: var(--primary-blue) !important;
text-decoration: underline;
}
footer {
background: var(--secondary-white);
color: var(--primary-red);
position: fixed;
bottom: 0;
width: 100%;
padding: 10px;
text-align: center;
}
.loader {
display: flex;
flex-direction: column;
align-items: center;
place-content: center;
}

17
src/css/label.css Normal file
View file

@ -0,0 +1,17 @@
.label {
padding: 4px;
}
.label-rounded {
border-radius: 5px;
}
.label-error {
background: var(--primary-red);
color: var(--primary-white);
}
.label-success {
background: var(--primary-green);
color: var(--primary-white);
}

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<head>
<title></title>
<link rel="stylesheet" href="/css/index.css"/>
<link rel="stylesheet" href="/css/label.css"/>
<link rel="stylesheet" href="/css/components/Response.css"/>
</head>
<body>
<div id="root"></div>
<footer>
Powered by <a href="https://clean.wtfismyip.com">clean.wtfismyip.com</a>
</footer>
<script src="/js/index.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/images/icons/icon16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

15
src/js/components/App.tsx Normal file
View file

@ -0,0 +1,15 @@
import React from "react";
import { ResponseRenderer } from "/components/ResponseRenderer";
import { useWebService } from "/hooks/useWebService";
export function App() {
const [response, error] = useWebService();
const t = chrome.i18n.getMessage;
if (response) {
return (<ResponseRenderer response={response}/>);
} else if (error) {
/* FIXME: Add ErrorRenderer */
} else {
return (<div className="loader">{t("loading")}</div>);
}
}

View file

@ -0,0 +1,39 @@
import React from "react";
import { TResponse } from "/lib/response";
export function ResponseRenderer({ response }: {response: TResponse}) {
const t = chrome.i18n.getMessage;
function YesOrNoLabel({yes}: {yes: boolean}) {
if (yes) {
return (<label className="label label-rounded label-success">{t("yes")}</label>);
} else {
return (<label className="label label-rounded label-error">{t("no")}</label>);
}
}
return (
<div className="response">
<div className="row">
<div>{t("ip_address")}</div>
<div>{response.IPAddress}</div>
</div>
<div className="row">
<div>{t("isp")}</div>
<div>{response.ISP}</div>
</div>
<div className="row">
<div>{t("city")}</div>
<div>{response.City}</div>
</div>
<div className="row">
<div>{t("country")}</div>
<div>{response.Country}</div>
</div>
<div className="row">
<div>{t("tor_exit_node")}</div>
<div>{<YesOrNoLabel yes={response.isTorExitNode}/>}</div>
</div>
</div>
);
}

View file

@ -0,0 +1,19 @@
import { TResponse, Response } from "/lib/response";
import { useEffect, useState } from "react";
type Maybe<T> = T | null;
export function useWebService(): [Maybe<TResponse>, Maybe<Error>] {
const endpoint = "https://wtfismyip.com/json";
const [response, setResponse] = useState<Maybe<TResponse>>(null);
const [error, setError] = useState<Maybe<Error>>(null);
useEffect(() => {
fetch(endpoint)
.then((res) => res.json())
.then((json) => setResponse(Response(json)))
.catch((err) => setError(err));
});
return [response, error];
}

8
src/js/index.tsx Normal file
View file

@ -0,0 +1,8 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "/components/App";
document.addEventListener("DOMContentLoaded", () => {
const el: HTMLElement = document.getElementById("root")!;
ReactDOM.createRoot(el).render(<App/>);
});

30
src/js/lib/response.ts Normal file
View file

@ -0,0 +1,30 @@
export type TResponse = {
IPAddress: string;
Location: string;
ISP: string;
City: string;
Country: string;
isTorExitNode: boolean;
};
export type TServerResponse = {
YourFuckingIPAddress: string;
YourFuckingLocation: string;
YourFuckingISP: string;
YourFuckingCity: string;
YourFuckingCountry: string;
YourFuckingTorExit: boolean;
};
export function Response(res: TServerResponse): TResponse {
const self = Object.create(null);
self.IPAddress = res.YourFuckingIPAddress;
self.Location = res.YourFuckingLocation;
self.ISP = res.YourFuckingISP;
self.City = res.YourFuckingCity;
self.Country = res.YourFuckingCountry;
self.isTorExitNode = res.YourFuckingTorExit;
return self;
}

15
src/manifest.json Normal file
View file

@ -0,0 +1,15 @@
{
"manifest_version": 3,
"name": "Photon",
"version": "0.1.0",
"description": "Provides various information about your public IP address.",
"action": {
"default_popup": "/html/browseraction.html"
},
"permissions": [],
"icons": {
"16": "images/icons/icon16.png",
"128": "images/icons/icon128.png"
},
"default_locale": "en"
}

17
tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"strict": true,
"strictNullChecks": false,
"module": "commonjs",
"target": "ES2020",
"noImplicitAny": true,
"moduleResolution": "node",
"esModuleInterop": true,
"jsx": "react",
"allowJs": true,
"lib": [ "ES2020", "DOM" ],
"baseUrl": "src/",
"paths": { "*": ["js/*"] },
}
}

42
webpack.config.js Normal file
View file

@ -0,0 +1,42 @@
const path = require('path');
const process = require('process');
const CopyPlugin = require("copy-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
mode: process.env.NODE_ENV || "development",
devtool: "inline-source-map",
entry: {
index: './src/js/index.tsx',
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'build')
},
resolve: {
roots: [path.resolve('src/js'), path.resolve('node_modules')],
modules: [path.resolve('src/js'), path.resolve('node_modules')],
extensions: ['.ts', '.tsx']
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/html/", to: "html" },
{ from: "src/css/", to: "css" },
{ from: "src/manifest.json", to: "manifest.json" },
{ from: "src/images", to: "images/" },
{ from: "src/_locales", to: "_locales/" }
],
}),
new CleanWebpackPlugin(),
],
}