diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 95f66629..a71a5642 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,13 @@ ## Requirements -- [x] You have completed your website. -- [x] The website is reachable. -- [x] The CNAME record doesn't contain `https://` or `/`. -- [x] There is sufficient information at the `owner` field. -- [x] There is no NS Records (Enforced as of Sepetember 4th, 2024) +- [ ] You have completed your website. +- [ ] The website is reachable. +- [ ] The CNAME record doesn't contain `https://` or `/`. +- [ ] There is sufficient information at the `owner` field. +- [ ] There is no NS Records (Enforced as of September 4th, 2024) +- [ ] I fully accept and undestand the [Terms of Service](https://github.com/open-domains/register/blob/main/terms.md) outline when using this service. +- [ ] I understand that if these requirements are not met my pull request will be closed. ## Description diff --git a/domains/ram-llc.is-local.org.json b/domains/ram-llc.is-local.org.json new file mode 100644 index 00000000..3a9a9901 --- /dev/null +++ b/domains/ram-llc.is-local.org.json @@ -0,0 +1,17 @@ +{ + "description": "", + + "domain": "is-local.org", + "subdomain": "ram-llc", + + "owner": { + "repo": "https://github.com/ShadowGaming100", + "email": "workspace.shadowgaming@gmail.com" + }, + + "record": { + "NS": ["darwin.ns.cloudflare.com", "mallory.ns.cloudflare.com"] + }, + + "proxied": false +} diff --git a/domains/wisard-gaming.is-not-a.dev.json b/domains/wisard-gaming.is-not-a.dev.json deleted file mode 100644 index 8bfeb0ba..00000000 --- a/domains/wisard-gaming.is-not-a.dev.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "description": "", - "domain": "is-not-a.dev", - "subdomain": "wisard-gaming", - "owner": { - "repo": "https://github.com/ShadowGaming100" - }, - "record": { - "NS": [ - "darwin.ns.cloudflare.com", - "mallory.ns.cloudflare.com" - ] - }, - "proxied": false -} \ No newline at end of file diff --git a/tests/json.test.js b/tests/json.test.js index c3274b6e..3c6b6434 100644 --- a/tests/json.test.js +++ b/tests/json.test.js @@ -11,6 +11,11 @@ const optionalOwnerFields = { email: "string" }; +const ignoredRootJSONFiles = ["package-lock.json", "package.json"]; + + +const domains = [".is-a-fullstack.dev", ".is-cool.dev", ".is-local.org", ".is-not-a.dev", ".localplayer.dev"]; + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; const hostnameRegex = /^(?=.{1,253}$)(?:(?:[_a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+[a-zA-Z]{2,63}$/; @@ -41,6 +46,24 @@ function validateOptionalFields(t, obj, optionalFields, file) { }); } +t("All JSON files must be valid JSON", (t) => { + const files = fs.readdirSync(domainsPath).filter(file => { + const filePath = path.join(domainsPath, file); + return file.endsWith(".json") && fs.lstatSync(filePath).isFile(); + }); + + files.forEach(file => { + const filePath = path.join(domainsPath, file); + try { + const data = fs.readFileSync(filePath, "utf8"); + JSON.parse(data); + t.pass(`${file} is valid JSON`); + } catch (err) { + t.fail(`${file} contains invalid JSON: ${err.message}`); + } + }); +}); + t("All files should have the required fields", (t) => { const files = fs.readdirSync(domainsPath).filter(file => { const filePath = path.join(domainsPath, file); @@ -73,3 +96,73 @@ t("All files should have valid optional owner fields", (t) => { } }); }); + +t("All files should have lowercase subdomain/domain and match filename", (t) => { + const files = fs.readdirSync(domainsPath).filter(file => { + const filePath = path.join(domainsPath, file); + return file.endsWith(".json") && fs.lstatSync(filePath).isFile(); + }); + + files.forEach(file => { + const filePath = path.join(domainsPath, file); + const data = fs.readJsonSync(filePath); + + const { subdomain, domain } = data; + + t.truthy(subdomain, `${file}: Missing subdomain field`); + t.truthy(domain, `${file}: Missing domain field`); + + t.is(subdomain, subdomain.toLowerCase(), `${file}: 'subdomain' must be lowercase`); + t.is(domain, domain.toLowerCase(), `${file}: 'domain' must be lowercase`); + + const expectedFileName = `${subdomain}.${domain}.json`; + t.is(file, expectedFileName, `${file}: Filename does not match subdomain and domain`); + }); +}); + +t("All files should use a valid root domain", (t) => { + const files = fs.readdirSync(domainsPath).filter(file => { + const filePath = path.join(domainsPath, file); + return file.endsWith(".json") && fs.lstatSync(filePath).isFile(); + }); + + files.forEach(file => { + const data = fs.readJsonSync(path.join(domainsPath, file)); + const domain = data.domain; + + t.truthy(domain, `${file}: Missing domain field`); + t.true( + domains.includes(`.${domain}`), + `${file}: Domain '${domain}' is not in the list of allowed domains` + ); + }); +}); + + +// Thanks is-a.dev for this. +t("JSON files should not be in the root directory", (t) => { + const rootFiles = fs + .readdirSync(path.resolve()) + .filter((file) => file.endsWith(".json") && !ignoredRootJSONFiles.includes(file)); + + if (rootFiles.length > 0) { + t.fail(`❌ JSON files should not be in the root directory: ${rootFiles.join(", ")}`); + } else { + t.pass(); + } +}); + +t("All files in the domains directory must have a .json extension", (t) => { + const files = fs.readdirSync(domainsPath).filter(file => { + const filePath = path.join(domainsPath, file); + return fs.lstatSync(filePath).isFile(); + }); + + const nonJsonFiles = files.filter(file => !file.endsWith(".json")); + + if (nonJsonFiles.length > 0) { + t.fail(`❌ Found non-JSON files in domains directory: ${nonJsonFiles.join(", ")}`); + } else { + t.pass(); + } +}); \ No newline at end of file