feat: add TOS
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-02-26 10:58:00 +00:00
parent bfb2b072f9
commit 912bb21022
10 changed files with 322 additions and 12 deletions

4
custom.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
declare module "*.md" {
const value: string;
export default value;
}

View File

@ -19,6 +19,7 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"classnames": "^2.5.1",
"marked": "^15.0.7",
"qr-code-styling": "^1.8.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@ -0,0 +1,39 @@
.markdown a {
@apply underline;
}
.markdown blockquote {
margin: 0;
padding-left: 12px;
@apply border-l-neutral-800 border-2 text-neutral-400;
}
.markdown hr {
border: 0;
height: 1px;
margin: 20px;
@apply bg-neutral-800;
}
.markdown img:not(.custom-emoji),
.markdown video,
.markdown iframe,
.markdown audio {
max-width: 100%;
display: block;
}
.markdown iframe,
.markdown video {
width: -webkit-fill-available;
aspect-ratio: 16 / 9;
}
.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
margin: 0.5em 0;
}

134
src/components/markdown.tsx Normal file
View File

@ -0,0 +1,134 @@
import "./markdown.css";
import { ReactNode, forwardRef, useMemo } from "react";
import { Token, Tokens, marked } from "marked";
import { Link } from "react-router-dom";
interface MarkdownProps {
content: string;
}
const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
let ctr = 0;
function renderToken(t: Token): ReactNode {
try {
switch (t.type) {
case "paragraph": {
return <div key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</div>;
}
case "image": {
return <img key={ctr++} src={t.href} />;
}
case "heading": {
switch (t.depth) {
case 1:
return <h1 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h1>;
case 2:
return <h2 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h2>;
case 3:
return <h3 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h3>;
case 4:
return <h4 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h4>;
case 5:
return <h5 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h5>;
case 6:
return <h6 key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</h6>;
}
throw new Error("Invalid heading");
}
case "codespan": {
return <code key={ctr++}>{t.raw}</code>;
}
case "code": {
return <pre key={ctr++}>{t.raw}</pre>;
}
case "br": {
return <br key={ctr++} />;
}
case "hr": {
return <hr key={ctr++} />;
}
case "strong": {
return <b key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</b>;
}
case "blockquote": {
return <blockquote key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</blockquote>;
}
case "link": {
return (
<Link to={t.href} key={ctr++}>
{t.tokens ? t.tokens.map(renderToken) : t.raw}
</Link>
);
}
case "list": {
if (t.ordered) {
return <ol key={ctr++}>{t.items.map(renderToken)}</ol>;
} else {
return <ul key={ctr++}>{t.items.map(renderToken)}</ul>;
}
}
case "list_item": {
return <li key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</li>;
}
case "em": {
return <em key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</em>;
}
case "del": {
return <s key={ctr++}>{t.tokens ? t.tokens.map(renderToken) : t.raw}</s>;
}
case "table": {
return (
<table className="table-auto border-collapse" key={ctr++}>
<thead>
<tr>
{(t.header as Tokens.TableCell[]).map(v => (
<th className="border" key={ctr++}>
{v.tokens ? v.tokens.map(renderToken) : v.text}
</th>
))}
</tr>
</thead>
<tbody>
{(t.rows as Tokens.TableCell[][]).map(v => (
<tr key={ctr++}>
{v.map((d, d_key) => (
<td className="border px-2 py-1" key={d_key}>
{d.tokens ? d.tokens.map(renderToken) : d.text}
</td>
))}
</tr>
))}
</tbody>
</table>
);
}
case "text": {
if ("tokens" in t) {
return (t.tokens as Array<Token>).map(renderToken);
}
return t.raw;
}
case "space": {
return " ";
}
default: {
console.debug(`Unknown token ${t.type}`);
}
}
} catch (e) {
console.error(e);
}
}
const parsed = useMemo(() => {
return marked.lexer(props.content);
}, [props.content]);
return (
<div className="markdown" ref={ref}>
{parsed.filter(a => a.type !== "footnote" && a.type !== "footnotes").map(a => renderToken(a))}
</div>
);
});
export default Markdown;

View File

@ -10,6 +10,7 @@ import OrderPage from "./pages/order.tsx";
import VmPage from "./pages/vm.tsx";
import AccountPage from "./pages/account.tsx";
import SignUpPage from "./pages/sign-up.tsx";
import { TosPage } from "./pages/terms.tsx";
const system = new NostrSystem({
automaticOutboxModel: false,
@ -46,6 +47,10 @@ const router = createBrowserRouter([
path: "/vm/:action?",
element: <VmPage />,
},
{
path: "/tos",
element: <TosPage />,
}
],
},
]);

View File

@ -3,6 +3,7 @@ import { VmTemplate, LNVpsApi } from "../api";
import Profile from "../components/profile";
import VpsCard from "../components/vps-card";
import { ApiUrl, NostrProfile } from "../const";
import { Link } from "react-router-dom";
export default function HomePage() {
const [offers, setOffers] = useState<Array<VmTemplate>>([]);
@ -25,20 +26,20 @@ export default function HomePage() {
<small>
All VPS come with 1x IPv4 and 1x IPv6 address and unmetered traffic
</small>
<div className="flex flex-col gap-4">
<b>You can also find us on nostr: </b>
<a target="_blank" href={`nostr:${NostrProfile.encode()}`}>
<div className="flex flex-col gap-6">
<a target="_blank" href={`https://snort.social/${NostrProfile.encode()}`}>
<Profile link={NostrProfile} />
</a>
<div>
<div className="text-center">
<a target="_blank" href="http://speedtest.v0l.io">
Speedtest
</a>
{" "}
{" | "}
<a href="/lnvps.asc">PGP</a>
{" "}
{" | "}
<a href="https://lnvps1.statuspage.io/" target="_blank">Status</a>
</div>
{" | "}
<Link to="/tos">Terms</Link>
</div>
<div className="text-xs text-center text-neutral-400">
LNVPS is a trading name of Apex Strata Ltd, a company registered in Ireland.
@ -47,6 +48,8 @@ export default function HomePage() {
Address: Suite 10628, 26/27 Upper Pembroke Street, Dublin 2, D02 X361, Ireland
</div>
</div>
</div>
</>
);
}

6
src/pages/terms.tsx Normal file
View File

@ -0,0 +1,6 @@
import Markdown from "../components/markdown";
import TOS from "../../tos.md?raw";
export function TosPage() {
return <Markdown content={TOS} />
}

107
tos.md Normal file
View File

@ -0,0 +1,107 @@
# Terms of Service
**LNVPS**
*Last Updated: February 26, 2025*
Welcome to LNVPS, a trading name of Apex Strata Ltd, a company registered in Ireland. These Terms of Service ("Terms") govern your use of our Virtual Private Server (VPS) hosting services, website, and related offerings (collectively, the "Services"). By accessing or using our Services, you agree to be bound by these Terms. If you do not agree, please do not use our Services.
---
## 1. Company Information
LNVPS is a trading name of Apex Strata Ltd, a company registered in Ireland.
- **Company Registration Number**: 702423
- **Registered Office**: Suite 10628, 26/27 Upper Pembroke Street, Dublin 2, D02 X361, Ireland
- **Email**: sales@lnvps.net
---
## 2. Definitions
- **"You" or "Customer"**: The individual or entity subscribing to or using the Services.
- **"We", "Us", or "LNVPS"**: Apex Strata Ltd, operating as LNVPS.
- **"Services"**: VPS hosting, support, and any additional features provided by LNVPS.
---
## 3. Eligibility
You must be at least 18 years old and capable of entering into a legally binding agreement to use our Services. By signing up, you confirm that all information provided is accurate and that you are authorized to act on behalf of any entity you represent.
---
## 4. Services
LNVPS provides VPS hosting services, including server resources, bandwidth, and technical support, as outlined on our website ([lnvps.net](https://lnvps.net) or applicable domain). Specific features, pricing, and resource limits are detailed in your chosen service plan at the time of purchase.
- **Service Availability**: We strive for 99.9% uptime but do not guarantee uninterrupted service. Downtime may occur due to maintenance, upgrades, or unforeseen events.
- **Modifications**: We reserve the right to modify or discontinue any aspect of the Services with reasonable notice.
---
## 5. Account Responsibilities
- **Account Security**: You are responsible for maintaining the confidentiality of your account credentials and for all activities under your account.
- **Usage**: You agree to use the Services only for lawful purposes and in compliance with these Terms.
- **Notification**: You must notify us immediately of any unauthorized use of your account.
---
## 6. Acceptable Use Policy
You agree not to use the Services to:
- Host, store, or distribute illegal content, including but not limited to pirated software, child exploitation material, or content inciting violence or hate.
- Engage in spamming, phishing, or other abusive activities.
- Overload or disrupt our servers, networks, or other customers services (e.g., DDoS attacks).
- Violate intellectual property rights or privacy laws.
We reserve the right to suspend or terminate your Services without notice if we detect violations, subject to applicable law.
---
## 7. Payment and Billing
- **Fees**: You agree to pay the fees for your chosen plan as outlined at checkout. All prices are in Euro (€) and include VAT where applicable.
- **Billing Cycle**: Payments are due in advance (monthly, quarterly, or annually, depending on your plan).
- **Late Payment**: Overdue accounts may be suspended until payment is received.
- **Refunds**: Refunds are available within 7 days of initial purchase, provided no excessive usage has occurred, as determined by us.
---
## 8. Termination
- **By You**: You may terminate your account at any time via your control panel or by contacting us, subject to the billing cycle terms.
- **By Us**: We may suspend or terminate your Services for non-payment, violation of these Terms, or if required by law, with or without notice depending on the severity of the breach.
- **Effect of Termination**: Upon termination, your access to the Services ends, and we may delete your data after 7 days unless otherwise required by law.
---
## 9. Data and Privacy
- **Your Data**: You retain ownership of data uploaded to your VPS. We do not access it except as needed to provide the Services or comply with legal obligations.
- **Backups**: You are responsible for maintaining backups of your data unless a backup service is included in your plan.
- **GDPR Compliance**: We process personal data in accordance with our [Privacy Policy](#), which complies with the General Data Protection Regulation (GDPR).
---
## 10. Limitation of Liability
To the fullest extent permitted by Irish law:
- Our liability for any claim arising from the Services is limited to the amount you paid us in the previous 12 months.
- We are not liable for indirect, consequential, or incidental damages (e.g., loss of profits, data, or business opportunities).
- We are not responsible for issues beyond our reasonable control, such as force majeure events (e.g., natural disasters, cyber-attacks).
---
## 11. Intellectual Property
- **Our IP**: The LNVPS website, branding, and software remain our property or that of our licensors.
- **Your IP**: You grant us a limited license to use your content solely to provide the Services.
---
## 12. Governing Law and Dispute Resolution
- These Terms are governed by the laws of Ireland.
- Any disputes will be subject to the exclusive jurisdiction of the courts of Ireland, though you may have additional rights under EU consumer law if applicable.
---
## 13. Changes to Terms
We may update these Terms from time to time. We will notify you of significant changes via email or on our website. Continued use of the Services after changes constitutes acceptance of the updated Terms.
---
## 14. Contact Us
For questions or support:
- **Email**: sales@lnvps.net
- **Address**: Suite 10628, 26/27 Upper Pembroke Street, Dublin 2, D02 X361, Ireland
---

View File

@ -4,4 +4,5 @@ import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
assetsInclude: ["**/*.md"],
});

View File

@ -2394,6 +2394,7 @@ __metadata:
eslint-plugin-react-hooks: "npm:^5.1.0-rc.0"
eslint-plugin-react-refresh: "npm:^0.4.9"
globals: "npm:^15.9.0"
marked: "npm:^15.0.7"
postcss: "npm:^8.4.41"
prettier: "npm:^3.3.3"
qr-code-styling: "npm:^1.8.4"
@ -2470,6 +2471,15 @@ __metadata:
languageName: node
linkType: hard
"marked@npm:^15.0.7":
version: 15.0.7
resolution: "marked@npm:15.0.7"
bin:
marked: bin/marked.js
checksum: 10c0/0b9d07bace37bbf0548bae356c4184765afa4d2296ed0be4418aa4bb0ce703f323dc1a475125d536581f9fe264797e6265dd0b57499d97c0fe0f29bc6d016343
languageName: node
linkType: hard
"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
version: 1.4.1
resolution: "merge2@npm:1.4.1"