@networkpro/web 1.5.0 β 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -62
- package/package.json +10 -9
- package/scripts/flattenHeaders.js +2 -2
- package/scripts/validateHeaders.js +1 -1
- package/src/app.html +1 -1
- package/src/lib/components/RedirectPage.svelte +62 -0
- package/src/lib/meta.js +1 -9
- package/src/lib/pages/TermsUseContent.svelte +5 -4
- package/src/lib/types/metadata.js +17 -0
- package/src/lib/utils/utm.js +19 -0
- package/src/routes/consultation/+page.svelte +34 -0
- package/src/routes/contact/+page.svelte +34 -0
- package/src/routes/privacy-rights/+page.svelte +34 -0
- package/tests/unit/utm.test.js +48 -0
- package/src/routes/contact/+page.server.js +0 -24
- package/src/routes/privacy-rights/+page.server.js +0 -24
package/README.md
CHANGED
|
@@ -1,116 +1,208 @@
|
|
|
1
|
-
|
|
2
|
-
README.md
|
|
1
|
+
# π Network Pro β Web Presence
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
========================================================================== -->
|
|
3
|
+
> **Locking Down Networks, Unlocking Confidenceβ’**
|
|
4
|
+
> _Security, Networking, Privacy β Network Proβ’_
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
Copyright Β© 2025 Network Pro Strategies (Network Pro)
|
|
6
|
+
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
## π Project Overview
|
|
9
|
+
|
|
10
|
+
This is the official web presence for **[Network Pro Strategies](https://netwk.pro/about)**, a privacy-forward consultancy specializing in network engineering, information security, and public advocacy focused on cybersecurity and digital privacy.
|
|
11
|
+
|
|
12
|
+
Built with [SvelteKit](https://kit.svelte.dev/) and deployed via [Netlify](https://www.netlify.com/).
|
|
13
|
+
Blog and documentation subsites built with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and deployed via [GitHub Pages](https://pages.github.com/).
|
|
14
|
+
All infrastructure and data flows are designed with **maximum transparency, self-hosting, and user privacy** in mind.
|
|
15
|
+
|
|
16
|
+
### π Repository Structure
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
```bash
|
|
19
|
+
.
|
|
20
|
+
βββ src/
|
|
21
|
+
β βββ lib/ # Reusable components, styles, utilities
|
|
22
|
+
β βββ routes/ # SvelteKit routes (+page.svelte, +page.server.js)
|
|
23
|
+
β βββ hooks.client.ts # Client-only lifecycle hooks (e.g., SW control)
|
|
24
|
+
β βββ app.html # SvelteKit entry HTML with CSP/meta/bootstrap
|
|
25
|
+
βββ tests/ # Vitest test suites
|
|
26
|
+
βββ public/ # Static assets served at root
|
|
27
|
+
βββ netlify.toml # Netlify configuration
|
|
28
|
+
βββ .github/ # CI workflows and automation
|
|
29
|
+
βββ ...
|
|
30
|
+
```
|
|
14
31
|
|
|
15
|
-
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## π Getting Started
|
|
35
|
+
|
|
36
|
+
### π¦ Environment Setup
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
```bash
|
|
39
|
+
git clone https://github.com/netwk-pro/netwk-pro.github.io.git
|
|
40
|
+
cd netwk-pro.github.io
|
|
41
|
+
cp .env.template .env
|
|
42
|
+
npm install
|
|
43
|
+
```
|
|
18
44
|
|
|
19
|
-
|
|
45
|
+
Edit .env to configure your environment mode:
|
|
20
46
|
|
|
21
|
-
|
|
47
|
+
```env
|
|
48
|
+
ENV_MODE=dev # Options: dev, test, ci, preview, prod
|
|
49
|
+
```
|
|
22
50
|
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
> ENV*MODE is used for tooling and workflows β not by SvelteKit itself.
|
|
52
|
+
> Use VITE*-prefixed env variables for runtime values.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### π Node.js Version Management
|
|
57
|
+
|
|
58
|
+
This repo uses .nvmrc and .node-version for version consistency with tools like:
|
|
59
|
+
|
|
60
|
+
- nvm
|
|
61
|
+
- asdf
|
|
62
|
+
- Volta
|
|
63
|
+
- GitHub Actions
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node -v # Should match "engines" in package.json
|
|
67
|
+
npm -v
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
|
|
25
71
|
|
|
26
72
|
---
|
|
27
73
|
|
|
28
|
-
|
|
74
|
+
|
|
29
75
|
|
|
30
|
-
|
|
76
|
+
## π Available Scripts
|
|
31
77
|
|
|
32
|
-
|
|
33
|
-
FITNESS FOR A PARTICULAR PURPOSE.
|
|
78
|
+
The following CLI commands are available via `npm run <script>` or `pnpm run <script>`.
|
|
34
79
|
|
|
35
|
-
|
|
80
|
+
### π Development
|
|
36
81
|
|
|
37
|
-
|
|
38
|
-
|
|
82
|
+
| Script | Description |
|
|
83
|
+
| --------------- | ---------------------------------- |
|
|
84
|
+
| `dev` | Start development server with Vite |
|
|
85
|
+
| `preview` | Preview production build locally |
|
|
86
|
+
| `build` | Build the project with Vite |
|
|
87
|
+
| `build:netlify` | Build using Netlify CLI |
|
|
88
|
+
| `css:bundle` | Bundle and minify CSS |
|
|
39
89
|
|
|
40
90
|
---
|
|
41
91
|
|
|
42
|
-
|
|
43
|
-
Email: <contact@neteng.pro>
|
|
44
|
-
Web: <https://bio.neteng.pro>
|
|
45
|
-
-->
|
|
92
|
+
### β
Pre-check / Sync
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
| Script | Description |
|
|
95
|
+
| ------------- | ------------------------------------------------------------ |
|
|
96
|
+
| `prepare` | Run SvelteKit sync |
|
|
97
|
+
| `check` | Run SvelteKit sync and type check with `svelte-check` |
|
|
98
|
+
| `check:watch` | Watch mode for type checks |
|
|
99
|
+
| `check:node` | Validate Node & NPM versions match package.json `engines` |
|
|
100
|
+
| `checkout` | Full local validation: check versions, test, lint, typecheck |
|
|
101
|
+
| `verify` | Alias for `checkout` |
|
|
48
102
|
|
|
49
|
-
|
|
103
|
+
---
|
|
50
104
|
|
|
51
|
-
|
|
105
|
+
### π§Ή Cleanup & Maintenance
|
|
52
106
|
|
|
53
|
-
|
|
107
|
+
| Script | Description |
|
|
108
|
+
| --------- | ----------------------------------------------- |
|
|
109
|
+
| `delete` | Remove build artifacts and `node_modules` |
|
|
110
|
+
| `clean` | Fully reset environment and reinstall |
|
|
111
|
+
| `upgrade` | Update all dependencies via `npm-check-updates` |
|
|
54
112
|
|
|
55
|
-
|
|
56
|
-
[](https://github.com/prettier/prettier) [](https://stylelint.io/)
|
|
57
|
-
[](https://github.com/netwk-pro/netwk-pro.github.io/blob/master/CODE_OF_CONDUCT.md)
|
|
113
|
+
---
|
|
58
114
|
|
|
59
|
-
|
|
115
|
+
### π§ͺ Testing
|
|
60
116
|
|
|
61
|
-
|
|
117
|
+
| Script | Description |
|
|
118
|
+
| --------------- | -------------------------------------------- |
|
|
119
|
+
| `test` | Alias for `test:all` |
|
|
120
|
+
| `test:all` | Run both client and server test suites |
|
|
121
|
+
| `test:client` | Run client tests with Vitest |
|
|
122
|
+
| `test:server` | Run server-side tests with Vitest |
|
|
123
|
+
| `test:watch` | Watch mode for client tests |
|
|
124
|
+
| `test:coverage` | Collect coverage from both client and server |
|
|
62
125
|
|
|
63
|
-
|
|
126
|
+
---
|
|
64
127
|
|
|
65
|
-
###
|
|
128
|
+
### π§Ό Linting & Formatting
|
|
66
129
|
|
|
67
|
-
|
|
130
|
+
| Script | Description |
|
|
131
|
+
| ------------ | --------------------------------------- |
|
|
132
|
+
| `lint` | Run ESLint on JS, MJS, and Svelte files |
|
|
133
|
+
| `lint:fix` | Auto-fix ESLint issues |
|
|
134
|
+
| `lint:jsdoc` | Check JSDoc annotations |
|
|
135
|
+
| `lint:css` | Run Stylelint on CSS and Svelte styles |
|
|
136
|
+
| `lint:md` | Lint Markdown content |
|
|
137
|
+
| `lint:all` | Run all linters and formatting checks |
|
|
138
|
+
| `format` | Run Prettier formatting check |
|
|
139
|
+
| `format:fix` | Auto-format code using Prettier |
|
|
68
140
|
|
|
69
|
-
|
|
70
|
-
- **Firewall Architecture & Policy Optimization**
|
|
71
|
-
- **Cloud Security & Zero Trust Implementation**
|
|
72
|
-
- **Secure Infrastructure Design & Implementation**
|
|
73
|
-
- **Risk Reduction & Security Posture Assessment**
|
|
141
|
+
---
|
|
74
142
|
|
|
75
|
-
|
|
143
|
+
### π‘ Lighthouse / Performance
|
|
76
144
|
|
|
77
|
-
|
|
145
|
+
| Script | Description |
|
|
146
|
+
| ------------------ | ----------------------------------------------- |
|
|
147
|
+
| `lhci` | Alias for Lighthouse CI |
|
|
148
|
+
| `lighthouse` | Run local Lighthouse test and launch viewer |
|
|
149
|
+
| `lighthouse:local` | Build site, preview, and run Lighthouse locally |
|
|
150
|
+
| `lhci:run` | Run Lighthouse CI autorun |
|
|
78
151
|
|
|
79
|
-
|
|
152
|
+
---
|
|
80
153
|
|
|
81
|
-
|
|
154
|
+
### π Audits / Validation
|
|
82
155
|
|
|
83
|
-
|
|
156
|
+
| Script | Description |
|
|
157
|
+
| --------------- | -------------------------------------------- |
|
|
158
|
+
| `audit:scripts` | Check for untested utility scripts |
|
|
159
|
+
| `head:flatten` | Flatten headers for Netlify |
|
|
160
|
+
| `head:validate` | Validate headers file against project config |
|
|
84
161
|
|
|
85
162
|
---
|
|
86
163
|
|
|
87
|
-
|
|
164
|
+
### π Lifecycle Hooks
|
|
88
165
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
| <img decoding="async" loading="lazy" src="https://netwk.pro/img/qr/vcard.png" width="125px" height="125px" alt="vCard"> | **vCard**<br /> <br /><a href="https://raw.githubusercontent.com/netwk-pro/netwk-pro.github.io/refs/heads/master/assets/bin/contact.vcf" download type="text/vcard">**vcf**</a> |
|
|
166
|
+
| Script | Description |
|
|
167
|
+
| ------------- | ----------------------------------- |
|
|
168
|
+
| `postinstall` | Ensures version check after install |
|
|
93
169
|
|
|
94
|
-
|
|
170
|
+
|
|
95
171
|
|
|
96
172
|
---
|
|
97
173
|
|
|
98
|
-
|
|
174
|
+
|
|
99
175
|
|
|
100
|
-
|
|
101
|
-
[Privacy Policy](https://netwk.pro/privacy-policy) | [Legal](https://netwk.pro/license)
|
|
176
|
+
## π§Ύ License
|
|
102
177
|
|
|
103
|
-
|
|
178
|
+
This project is licensed under:
|
|
179
|
+
|
|
180
|
+
- [Creative Commons BY 4.0](https://netwk.pro/license#cc-by)
|
|
181
|
+
|
|
182
|
+
- Or optionally, [GNU GPL v3 or later](https://netwk.pro/license#gnu-gpl)
|
|
183
|
+
|
|
184
|
+
Source code, branding, and visual assets are subject to reuse and distribution terms specified on our [Legal, Copyright, and Licensing page](https://netwk.pro/license).
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
## πββοΈ Questions?
|
|
189
|
+
|
|
190
|
+
Reach out via [netwk.pro/contact](https://netwk.pro/contact), open an issue on this repo, or email us directly at `contact (at) s.neteng.pro`.
|
|
104
191
|
|
|
105
192
|
|
|
106
193
|
|
|
107
|
-
|
|
194
|
+
_Designed for professionals. Hardened for privacy. Built with intent._
|
|
195
|
+
β **Network Pro Strategies**
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
<div style="font-size: 12px; text-align: center;">
|
|
108
200
|
|
|
109
201
|
Copyright © 2025
|
|
110
|
-
**[Network Pro Strategies](https://netwk.pro
|
|
202
|
+
**[Network Pro Strategies](https://netwk.pro) (Network Pro™)**
|
|
111
203
|
|
|
112
204
|
Network Pro™, the shield logo, and the "Locking Down Networks™" slogan are [trademarks](https://netwk.pro/license#trademark) of Network Pro Strategies.
|
|
113
205
|
|
|
114
|
-
Licensed under **[CC BY 4.0](https://netwk.pro/license#cc-by)** and the **[GNU GPL](https://netwk.pro/license#gnu-gpl)**, as published by the [Free Software Foundation](https://fsf.org), either version 3 of the License, or (at your option) any later version.
|
|
206
|
+
Licensed under **[CC BY 4.0](https://netwk.pro/license#cc-by)** and the **[GNU GPL](https://netwk.pro/license#gnu-gpl)**, as published by the [Free Software Foundation](https://www.fsf.org), either version 3 of the License, or (at your option) any later version.
|
|
115
207
|
|
|
116
|
-
</
|
|
208
|
+
</div>
|
package/package.json
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
"name": "@networkpro/web",
|
|
3
3
|
"private": false,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "1.5.
|
|
5
|
+
"version": "1.5.1",
|
|
6
6
|
"description": "Locking Down Networks, Unlocking Confidence | Security, Networking, Privacy β Network Pro Strategies",
|
|
7
7
|
"keywords": [
|
|
8
|
-
"security",
|
|
9
|
-
"networking",
|
|
10
|
-
"privacy",
|
|
11
|
-
"cybersecurity",
|
|
12
8
|
"advisory",
|
|
13
9
|
"consulting",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
10
|
+
"cybersecurity",
|
|
11
|
+
"networking",
|
|
12
|
+
"privacy",
|
|
13
|
+
"pwa",
|
|
14
|
+
"security",
|
|
15
|
+
"svelte",
|
|
16
|
+
"sveltekit"
|
|
16
17
|
],
|
|
17
18
|
"homepage": "https://netwk.pro",
|
|
18
19
|
"bugs": {
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
"postinstall": "npm run check:node"
|
|
85
86
|
},
|
|
86
87
|
"dependencies": {
|
|
87
|
-
"svelte": "5.
|
|
88
|
+
"svelte": "5.32.1"
|
|
88
89
|
},
|
|
89
90
|
"devDependencies": {
|
|
90
91
|
"@eslint/compat": "^1.2.9",
|
|
@@ -102,7 +103,7 @@
|
|
|
102
103
|
"eslint": "^9.27.0",
|
|
103
104
|
"eslint-config-prettier": "^10.1.5",
|
|
104
105
|
"eslint-plugin-jsdoc": "^50.6.17",
|
|
105
|
-
"eslint-plugin-svelte": "^3.
|
|
106
|
+
"eslint-plugin-svelte": "^3.9.0",
|
|
106
107
|
"globals": "^16.1.0",
|
|
107
108
|
"jsdom": "^26.1.0",
|
|
108
109
|
"lightningcss": "^1.30.1",
|
|
@@ -16,8 +16,8 @@ This file is part of Network Pro.
|
|
|
16
16
|
|
|
17
17
|
import fs from "fs";
|
|
18
18
|
|
|
19
|
-
const HEADERS_PATH = "
|
|
20
|
-
const OUTPUT_PATH = "./
|
|
19
|
+
const HEADERS_PATH = "./.headers_new"; // update if needed
|
|
20
|
+
const OUTPUT_PATH = "./_headers.flattened";
|
|
21
21
|
|
|
22
22
|
const lines = fs.readFileSync(HEADERS_PATH, "utf-8").split("\n");
|
|
23
23
|
const output = [];
|
package/src/app.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
|
17
17
|
<meta name="author" content="Network Pro Strategies" />
|
|
18
18
|
|
|
19
|
-
<!--
|
|
19
|
+
<!-- CSP for Dev -->
|
|
20
20
|
<meta
|
|
21
21
|
http-equiv="Content-Security-Policy"
|
|
22
22
|
content="default-src 'self'; script-src 'self' 'unsafe-inline' https://snap.licdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://px.ads.linkedin.com; connect-src 'self' https://px.ads.linkedin.com;" />
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/lib/components/RedirectPage.svelte
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== -->
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { onMount } from "svelte";
|
|
10
|
+
|
|
11
|
+
export let to;
|
|
12
|
+
export let delay = 3;
|
|
13
|
+
|
|
14
|
+
onMount(() => {
|
|
15
|
+
if (!to) {
|
|
16
|
+
console.warn("β No redirect target provided");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log("β
Starting redirect to:", to);
|
|
21
|
+
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
window.location.href = to;
|
|
24
|
+
}, delay * 1000);
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<svelte:head>
|
|
29
|
+
<title>Redirectingβ¦</title>
|
|
30
|
+
<meta name="robots" content="noindex, nofollow" />
|
|
31
|
+
</svelte:head>
|
|
32
|
+
|
|
33
|
+
<div class="container">
|
|
34
|
+
<h1>Redirectingβ¦</h1>
|
|
35
|
+
<p>You'll be taken to the destination in just a moment.</p>
|
|
36
|
+
<div class="loading-spinner" aria-hidden="true"></div>
|
|
37
|
+
<p>If nothing happens, <a href={to}>click here</a>.</p>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.loading-spinner {
|
|
42
|
+
width: 48px;
|
|
43
|
+
height: 48px;
|
|
44
|
+
margin: 2rem auto;
|
|
45
|
+
border: 4px solid #ddd;
|
|
46
|
+
animation: spin 1s linear infinite;
|
|
47
|
+
border-radius: 50%;
|
|
48
|
+
border-top: 4px solid #ffc627;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@keyframes spin {
|
|
52
|
+
to {
|
|
53
|
+
transform: rotate(360deg);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.container {
|
|
58
|
+
text-align: center;
|
|
59
|
+
font-family: system-ui, sans-serif;
|
|
60
|
+
margin-top: 5rem;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
package/src/lib/meta.js
CHANGED
|
@@ -50,15 +50,7 @@ const meta = {
|
|
|
50
50
|
description:
|
|
51
51
|
"FOSS Spotlight | Security, Networking, Privacy β Network Proβ’",
|
|
52
52
|
},
|
|
53
|
-
|
|
54
|
-
title: "Contact Form β Network Proβ’",
|
|
55
|
-
description: "Contact Form | Security, Networking, Privacy β Network Proβ’",
|
|
56
|
-
},
|
|
57
|
-
"/privacy-rights": {
|
|
58
|
-
title: "Privacy Rights Request Form β Network Proβ’",
|
|
59
|
-
description:
|
|
60
|
-
"Privacy Rights Request Form | Security, Networking, Privacy β Network Proβ’",
|
|
61
|
-
},
|
|
53
|
+
// Excludes redirect-only routes like /contact, /consultation, /privacy-rights
|
|
62
54
|
};
|
|
63
55
|
|
|
64
56
|
/** @type {MetaData} */
|
|
@@ -58,7 +58,7 @@ This file is part of Network Pro.
|
|
|
58
58
|
*/
|
|
59
59
|
const constants = {
|
|
60
60
|
company: "Network Pro Strategies",
|
|
61
|
-
effectiveDate: "May
|
|
61
|
+
effectiveDate: "May 21, 2025",
|
|
62
62
|
classSmall: "small-text",
|
|
63
63
|
rel: "noopener noreferrer",
|
|
64
64
|
hrefTop: "#top",
|
|
@@ -141,9 +141,10 @@ This file is part of Network Pro.
|
|
|
141
141
|
These Terms of Use apply to all platforms associated with the Company,
|
|
142
142
|
including but not limited to:
|
|
143
143
|
<strong>
|
|
144
|
-
GitHub, our main website (hosted via GitHub Pages),
|
|
145
|
-
|
|
146
|
-
media presence (e.g., Facebook, Instagram, X, and similar
|
|
144
|
+
GitHub, our main website (hosted via Netlify and GitHub Pages), Stack
|
|
145
|
+
Overflow Teams, Nextcloud, communications on Discord and/or Slack, and
|
|
146
|
+
our social media presence (e.g., Facebook, Instagram, X, and similar
|
|
147
|
+
platforms).
|
|
147
148
|
</strong>
|
|
148
149
|
</p>
|
|
149
150
|
{:else if link.id === "acceptable-use"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/types/metadata.js
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} MetaData
|
|
10
|
+
* @property {string} title - Page title
|
|
11
|
+
* @property {string} description - Meta description for SEO/social
|
|
12
|
+
* @property {string} [image] - Optional OG image URL
|
|
13
|
+
* @property {string} [url] - Canonical URL
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Export types only for JSDoc
|
|
17
|
+
export /** @typedef {import('./metadata.js').MetaData} MetaData */ {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
src/lib/utils/utm.js
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Append UTM parameter from window.location to a given URL.
|
|
10
|
+
* Returns `null` if not in a browser context.
|
|
11
|
+
* @param {string} url - The base URL to append to
|
|
12
|
+
* @returns {string | null}
|
|
13
|
+
*/
|
|
14
|
+
export function appendUTM(url) {
|
|
15
|
+
if (typeof window === "undefined") return null;
|
|
16
|
+
|
|
17
|
+
const utm = new URLSearchParams(window.location.search).get("utm_source");
|
|
18
|
+
return utm ? `${url}?utm_source=${encodeURIComponent(utm)}` : url;
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/consultation/+page.svelte
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== -->
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/appointments/pub/8clCqQrt3AtGbNrr/form",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirectβ¦</p>
|
|
34
|
+
{/if}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/contact/+page.svelte
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== -->
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/forms/s/nyWEq9fdE7kWAjqMtMySLqJc",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirectβ¦</p>
|
|
34
|
+
{/if}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- ==========================================================================
|
|
2
|
+
src/routes/privacy-rights/+page.svelte
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== -->
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import RedirectPage from "$lib/components/RedirectPage.svelte";
|
|
10
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
11
|
+
import { onMount } from "svelte";
|
|
12
|
+
import { browser } from "$app/environment";
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
let target = null;
|
|
16
|
+
|
|
17
|
+
/** @type {boolean} */
|
|
18
|
+
let show = false;
|
|
19
|
+
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!browser) return;
|
|
22
|
+
|
|
23
|
+
target = appendUTM(
|
|
24
|
+
"https://cloud.neteng.pro/index.php/apps/forms/s/6HpZKZCaLwb6TXYL99nLQM8t",
|
|
25
|
+
);
|
|
26
|
+
show = true;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
{#if show && target}
|
|
31
|
+
<RedirectPage to={target} />
|
|
32
|
+
{:else}
|
|
33
|
+
<p style="text-align: center; margin-top: 4rem;">Preparing to redirectβ¦</p>
|
|
34
|
+
{/if}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
tests/unit/utm.test.js
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
+
This file is part of Network Pro.
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
import { appendUTM } from "$lib/utils/utm.js";
|
|
9
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
10
|
+
|
|
11
|
+
describe("appendUTM", () => {
|
|
12
|
+
const originalWindow = globalThis.window;
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
globalThis.window = originalWindow;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should return null when not in a browser environment", () => {
|
|
19
|
+
// @ts-expect-error β simulating SSR
|
|
20
|
+
delete globalThis.window;
|
|
21
|
+
|
|
22
|
+
const url = "https://example.com";
|
|
23
|
+
const result = appendUTM(url);
|
|
24
|
+
expect(result).toBe(null);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return URL with utm_source appended", () => {
|
|
28
|
+
globalThis.window = {
|
|
29
|
+
// @ts-expect-error β mock minimal window for test
|
|
30
|
+
location: { search: "?utm_source=linkedin" },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const url = "https://example.com";
|
|
34
|
+
const result = appendUTM(url);
|
|
35
|
+
expect(result).toBe("https://example.com?utm_source=linkedin");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should return original URL if no utm_source is present", () => {
|
|
39
|
+
globalThis.window = {
|
|
40
|
+
// @ts-expect-error β mock minimal window for test
|
|
41
|
+
location: { search: "" },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const url = "https://example.com";
|
|
45
|
+
const result = appendUTM(url);
|
|
46
|
+
expect(result).toBe("https://example.com");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/* ==========================================================================
|
|
2
|
-
src/routes/contact/+page.server.js
|
|
3
|
-
|
|
4
|
-
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
-
This file is part of Network Pro.
|
|
6
|
-
========================================================================== */
|
|
7
|
-
|
|
8
|
-
import { redirect } from "@sveltejs/kit";
|
|
9
|
-
|
|
10
|
-
export const prerender = false;
|
|
11
|
-
|
|
12
|
-
/** @type {import('./$types').PageServerLoad} */
|
|
13
|
-
export function load({ url }) {
|
|
14
|
-
const utmSource = url.searchParams.get("utm_source");
|
|
15
|
-
|
|
16
|
-
// Set target to your actual contact page path
|
|
17
|
-
const target =
|
|
18
|
-
"https://cloud.neteng.pro/index.php/apps/forms/s/nyWEq9fdE7kWAjqMtMySLqJc";
|
|
19
|
-
|
|
20
|
-
// Preserve the tracking parameter
|
|
21
|
-
const redirectTo = utmSource ? `${target}?utm_source=${utmSource}` : target;
|
|
22
|
-
|
|
23
|
-
throw redirect(302, redirectTo);
|
|
24
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/* ==========================================================================
|
|
2
|
-
src/routes/privacy-rights/+page.server.js
|
|
3
|
-
|
|
4
|
-
SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
|
|
5
|
-
This file is part of Network Pro.
|
|
6
|
-
========================================================================== */
|
|
7
|
-
|
|
8
|
-
import { redirect } from "@sveltejs/kit";
|
|
9
|
-
|
|
10
|
-
export const prerender = false;
|
|
11
|
-
|
|
12
|
-
/** @type {import('./$types').PageServerLoad} */
|
|
13
|
-
export function load({ url }) {
|
|
14
|
-
const utmSource = url.searchParams.get("utm_source");
|
|
15
|
-
|
|
16
|
-
// Set target to your actual privacy rights form path
|
|
17
|
-
const target =
|
|
18
|
-
"https://cloud.neteng.pro/index.php/apps/forms/s/6HpZKZCaLwb6TXYL99nLQM8t";
|
|
19
|
-
|
|
20
|
-
// Preserve the tracking parameter
|
|
21
|
-
const redirectTo = utmSource ? `${target}?utm_source=${utmSource}` : target;
|
|
22
|
-
|
|
23
|
-
throw redirect(302, redirectTo);
|
|
24
|
-
}
|