WIP
parent
a93e3cf8dd
commit
e9d0abba70
@ -1 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"}
|
||||
{"version":3,"sourceRoot":"","sources":["../../src/scss/style.scss"],"names":[],"mappings":"AAIA;EACC,kBALqB;EAMrB;EACA;;;AAGD;EACC;;;AAOD;EACC;;;AAGD;EACC,kBAtBqB;;AAwBrB;EACC;EACA;;AAGD;EACC;;AAIA;EACC;EACA;EACA;;AAIA;AAEC;EACA,kBA3CqB;EA4CrB;;AAKD;EACC;EACA;;;AAMJ;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;;AAKD;EACC,kBArFmB;EAsFnB;EACA;;;AAKH;EACC;;AAEA;EACC;EACA;;AAGD;EACC;;AAGD;EACC;;;AAKD;EACC;;AAIA;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;AAKD;EACC;;AAIF;EACC;EACA;;AAEA;EACC;;AAGD;EACC;;AAIF;EACC;;AAEA;EACC;;AAKD;EACC;;AAGD;EACC;;AAGD;EACC;;AAGD;EACC;;;AAKH;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC","file":"style.css"}
|
@ -1,197 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const graphql = require("graphql");
|
||||
const util = require("util");
|
||||
const chalk = require("chalk");
|
||||
|
||||
const gql = require("./graphql/tag");
|
||||
const api = require("./api/index");
|
||||
|
||||
function debugDisplay(results) {
|
||||
if (results.errors != null && results.errors.length > 0) {
|
||||
results.errors.forEach((graphqlError) => {
|
||||
let errorHeader;
|
||||
|
||||
if (graphqlError.path != null) {
|
||||
errorHeader = `Error occurred for path: ${graphqlError.path.join(" -> ")}`;
|
||||
} else if (graphqlError.locations != null && graphqlError.locations.length > 0) {
|
||||
errorHeader = `Error occurred at line ${graphqlError.locations[0].line}, column ${graphqlError.locations[0].column}`;
|
||||
} else {
|
||||
errorHeader = "Error occurred in GraphQL";
|
||||
}
|
||||
|
||||
console.log(chalk.bgBlue.white(errorHeader));
|
||||
|
||||
let error = graphqlError.originalError;
|
||||
|
||||
if (error != null) {
|
||||
if (error.showChain != null) {
|
||||
console.log(error.showChain());
|
||||
} else {
|
||||
console.log(error.stack);
|
||||
}
|
||||
} else {
|
||||
console.log(graphqlError.stack);
|
||||
}
|
||||
|
||||
console.log("-----------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
console.log(util.inspect(results.data, {colors: true, depth: null}));
|
||||
}
|
||||
|
||||
// ###############################################
|
||||
|
||||
let makeQuery = api();
|
||||
|
||||
// FIXME: If we intend to target macOS, a lot of whitespace-based output splitting won't work: https://www.mail-archive.com/austin-group-l@opengroup.org/msg01678.html
|
||||
|
||||
// findmnt --json -o +SIZE,AVAIL
|
||||
// -> map back to mountPoint stuff?
|
||||
// blkid
|
||||
// to discover the filesystem that a given path exists on: stat -c %m
|
||||
// partx
|
||||
// (rest of util-linux)
|
||||
// memory usage: /proc/meminfo
|
||||
|
||||
return Promise.try(() => {
|
||||
let query = gql`
|
||||
query {
|
||||
hardware {
|
||||
drives {
|
||||
smartHealth
|
||||
size
|
||||
rpm
|
||||
serialNumber
|
||||
model
|
||||
modelFamily
|
||||
firmwareVersion
|
||||
|
||||
blockDevice {
|
||||
name
|
||||
}
|
||||
|
||||
partitions: allBlockDevices(type: PARTITION) {
|
||||
name
|
||||
size
|
||||
|
||||
mounts {
|
||||
mountpoint
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// let query = gql`
|
||||
// # query SomeDrives($drivePaths: [String]) {
|
||||
// query SomeDrives {
|
||||
// hardware {
|
||||
// drives {
|
||||
// path
|
||||
// interface
|
||||
|
||||
// model
|
||||
// modelFamily
|
||||
|
||||
// blockDevice {
|
||||
// submounts: mounts(type: SUBMOUNT) {
|
||||
// mountpoint
|
||||
// filesystem
|
||||
// }
|
||||
// }
|
||||
// # smartAvailable
|
||||
// # smartEnabled
|
||||
// # serialNumber
|
||||
// # wwn
|
||||
// # firmwareVersion
|
||||
// # size
|
||||
// # rpm
|
||||
// # logicalSectorSize
|
||||
// # physicalSectorSize
|
||||
// # formFactor
|
||||
// # ataVersion
|
||||
// # sataVersion
|
||||
|
||||
// # smartHealth
|
||||
// # smartAttributes {
|
||||
// # name
|
||||
// # type
|
||||
// # value
|
||||
// # failingNow
|
||||
|
||||
// # flags {
|
||||
// # affectsPerformance
|
||||
// # indicatesFailure
|
||||
// # }
|
||||
// # }
|
||||
|
||||
// # blockDevice {
|
||||
// # removable
|
||||
|
||||
// # children {
|
||||
// # name
|
||||
// # mountpoint
|
||||
// # size
|
||||
// # }
|
||||
// # }
|
||||
// }
|
||||
// }
|
||||
|
||||
// # resources {
|
||||
// # blockDevices {
|
||||
// # name
|
||||
// # mountpoint
|
||||
// # size
|
||||
// # deviceNumber
|
||||
// # removable
|
||||
// # readOnly
|
||||
// # parent { name }
|
||||
|
||||
// # children {
|
||||
// # name
|
||||
// # mountpoint
|
||||
// # size
|
||||
// # deviceNumber
|
||||
// # removable
|
||||
// # readOnly
|
||||
// # parent { name }
|
||||
// # }
|
||||
// # }
|
||||
|
||||
// # lvm {
|
||||
// # physicalVolumes {
|
||||
// # path
|
||||
|
||||
// # blockDevice {
|
||||
// # name
|
||||
// # deviceNumber
|
||||
// # }
|
||||
|
||||
// # volumeGroup {
|
||||
// # name
|
||||
// # }
|
||||
|
||||
// # format
|
||||
// # size
|
||||
// # freeSpace
|
||||
// # duplicate
|
||||
// # allocatable
|
||||
// # used
|
||||
// # exported
|
||||
// # missing
|
||||
// # }
|
||||
// # }
|
||||
// # }
|
||||
// }
|
||||
// `;
|
||||
|
||||
return makeQuery(query, {
|
||||
// drivePaths: ["/dev/sda", "/dev/sdb"]
|
||||
});
|
||||
}).then((results) => {
|
||||
debugDisplay(results);
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
// Like Object.fromEntries, but for {key,value} instead of [key,value]
|
||||
|
||||
module.exports = function fromNamedEntries(namedEntries) {
|
||||
return Object.fromEntries(namedEntries.map((entry) => {
|
||||
let { key, value } = entry;
|
||||
return [ key, value ];
|
||||
}));
|
||||
};
|
@ -1,428 +0,0 @@
|
||||
scalar ByteSize
|
||||
scalar TimeSize
|
||||
|
||||
type AccessPermissions {
|
||||
read: Boolean
|
||||
write: Boolean
|
||||
execute: Boolean
|
||||
}
|
||||
|
||||
type Permissions {
|
||||
owner: AccessPermissions
|
||||
group: AccessPermissions
|
||||
everybody: AccessPermissions
|
||||
setUID: Boolean
|
||||
setGID: Boolean
|
||||
sticky: Boolean
|
||||
}
|
||||
|
||||
type User {
|
||||
name: String
|
||||
id: Int
|
||||
# FIXME
|
||||
}
|
||||
|
||||
type Group {
|
||||
name: String
|
||||
id: Int
|
||||
# FIXME
|
||||
}
|
||||
|
||||
type RawMountValueOption {
|
||||
key: String!
|
||||
value: String
|
||||
}
|
||||
|
||||
type RawMountFlagOption {
|
||||
key: String!
|
||||
}
|
||||
|
||||
union RawMountOption = RawMountFlagOption | RawMountValueOption
|
||||
|
||||
enum MountErrorHandlingMode {
|
||||
PANIC
|
||||
CONTINUE
|
||||
REMOUNT_READ_ONLY
|
||||
}
|
||||
|
||||
enum ExtAllocator {
|
||||
OLD
|
||||
ORLOV
|
||||
}
|
||||
|
||||
enum ExtJournalingMode {
|
||||
JOURNAL
|
||||
ORDERED
|
||||
WRITEBACK
|
||||
}
|
||||
|
||||
enum ExtBufferErrorHandlingMode {
|
||||
IGNORE_ERROR
|
||||
ABORT_JOURNAL
|
||||
}
|
||||
|
||||
enum ExtQuotaSystem {
|
||||
OLD
|
||||
V0
|
||||
V1
|
||||
}
|
||||
|
||||
enum FatTimestampsAllowedFrom {
|
||||
EVERYBODY
|
||||
GROUP
|
||||
OWNER
|
||||
}
|
||||
|
||||
enum FatNameStrictness {
|
||||
RELAXED
|
||||
NORMAL
|
||||
STRICT
|
||||
}
|
||||
|
||||
enum FatShortNameMode {
|
||||
LOWER
|
||||
WINDOWS_95
|
||||
WINDOWS_NT
|
||||
MIXED
|
||||
}
|
||||
|
||||
enum FatLineEndingConversionPolicy {
|
||||
BINARY
|
||||
TEXT
|
||||
AUTO
|
||||
}
|
||||
|
||||
type MountOptions {
|
||||
writable: Boolean
|
||||
userMountable: Boolean
|
||||
freelyMountable: Boolean
|
||||
|
||||
asynchronous: Boolean
|
||||
asynchronousDirectoryUpdates: Boolean
|
||||
|
||||
allowDeviceNodes: Boolean
|
||||
allowExecution: Boolean
|
||||
allowMandatoryLocks: Boolean
|
||||
allowSetUIDBits: Boolean
|
||||
|
||||
requiresNetworkAccess: Boolean
|
||||
allowOfflineChecks: Boolean
|
||||
|
||||
updateAccessTime: Boolean
|
||||
updateDirectoryAccessTime: Boolean
|
||||
updateAccessTimeRelatively: Boolean
|
||||
updateAccessTimeStrictly: Boolean
|
||||
|
||||
automaticallyMountable: Boolean
|
||||
incrementIVersion: Boolean
|
||||
printDebugInformation: Boolean
|
||||
reportMountingErrors: Boolean
|
||||
onError: MountErrorHandlingMode
|
||||
|
||||
owner: User
|
||||
group: Group
|
||||
|
||||
filesystemOwner: User
|
||||
filesystemGroup: Group
|
||||
filesystemPermissions: Permissions
|
||||
|
||||
# devpts
|
||||
newPTYOwner: User
|
||||
newPTYGroup: Group
|
||||
newPTYPermissions: Permissions
|
||||
|
||||
isolatePTYs: Boolean
|
||||
ptmxPermissions: Permissions
|
||||
|
||||
# tmpfs, hugetlbfs
|
||||
rootOwner: User
|
||||
rootGroup: Group
|
||||
rootPermissions: Permissions
|
||||
maximumInodes: Int
|
||||
size: ByteSize
|
||||
|
||||
# hugetlbfs
|
||||
pageSize: ByteSize
|
||||
sizeAsPoolPercentage: Float
|
||||
minimumSize: ByteSize
|
||||
minimumSizeAsPoolPercentage: Float
|
||||
|
||||
# tmpfs
|
||||
blockCount: Int
|
||||
numaPolicy: String
|
||||
|
||||
# ext2, ext3, ext4
|
||||
allow32bitIdentifiers: Boolean
|
||||
allow64bitInodeVersions: Boolean
|
||||
allowExtendedAttributes: Boolean
|
||||
|
||||
supportACL: Boolean
|
||||
showOnlyUsableSpace: Boolean
|
||||
useGroupIDFromDirectory: Boolean
|
||||
|
||||
allowAutomaticFilesystemResizing: Boolean
|
||||
enableMetadataBlockTracking: Boolean
|
||||
simulateAbort: Boolean
|
||||
attachBufferHeads: Boolean # obsolete
|
||||
checkFilesystemOnMount: Boolean
|
||||
enableWriteBarriers: Boolean
|
||||
enableHardwareDeleteCalls: Boolean
|
||||
|
||||
allocator: ExtAllocator
|
||||
reservedSpaceForGroup: Group
|
||||
reservedSpaceForUser: User
|
||||
superblockIndex: Int
|
||||
inodeBlockReadAheadLimit: Int
|
||||
stripeBlocks: Int
|
||||
directorySizeLimit: ByteSize
|
||||
|
||||
updateJournalFormat: Boolean
|
||||
enableJournalChecksumming: Boolean
|
||||
asynchronousJournalCommits: Boolean
|
||||
processJournal: Boolean
|
||||
journalInode: Int
|
||||
journalDevice: String # FIXME: Translate this into a BlockDevice somehow? Expected 'number' format here is unclear.
|
||||
journalPath: String
|
||||
journalingMode: ExtJournalingMode
|
||||
journalIOPriority: Int
|
||||
onBufferError: ExtBufferErrorHandlingMode
|
||||
commitInterval: TimeSize
|
||||
|
||||
allowDeferredAllocations: Boolean
|
||||
batchingTimeLimit: TimeSize
|
||||
batchingTimeMinimum: TimeSize
|
||||
enableMetadataBlockCache: Boolean
|
||||
enableDirectIOReadLocking: Boolean
|
||||
enableInodeTableBlockInitialization: Boolean
|
||||
inodeTableBlockInitializationDelay: Int
|
||||
automaticallySynchronizeBeforeRename: Boolean
|
||||
|
||||
enableGroupQuota: Boolean
|
||||
enableUserQuota: Boolean
|
||||
enableProjectQuota: Boolean
|
||||
quotaSystem: ExtQuotaSystem
|
||||
userQuotaFile: String
|
||||
groupQuotaFile: String
|
||||
|
||||
# fat, vfat
|
||||
blockSize: ByteSize
|
||||
allocationTableBitness: Int
|
||||
compressedVolumeFileModule: String
|
||||
compressedVolumeFileOption: String
|
||||
|
||||
defaultFileMode: Permissions
|
||||
defaultFolderMode: Permissions
|
||||
nameStrictness: FatNameStrictness
|
||||
timestampChangesAllowedFrom: FatTimestampsAllowedFrom
|
||||
restrictExecutableModeToWindowsBinaries: Boolean
|
||||
treatAttrSysFlagAsImmutable: Boolean
|
||||
|
||||
allowDifferentlyCasedNameConflicts: Boolean
|
||||
shortNameMode: FatShortNameMode
|
||||
preferShortNamesWithoutSequenceNumber: Boolean
|
||||
enableUnicodeCharacterEscaping: Boolean
|
||||
supportUtf8: Boolean
|
||||
|
||||
enableReadOnlyFlagSupport: Boolean
|
||||
enableDOS1xFallbackConfiguration: Boolean
|
||||
enableQuietModeFailures: Boolean
|
||||
enableNFSInodeCache: Boolean
|
||||
enableEagerFlushing: Boolean
|
||||
enableFreeClusterCache: Boolean
|
||||
enableTimezoneConversion: Boolean
|
||||
|
||||
timestampOffset: TimeSize
|
||||
lineEndingConversionPolicy: FatLineEndingConversionPolicy
|
||||
conversionCharacterSet: String
|
||||
codepage: Int
|
||||
}
|
||||
|
||||
enum MountType {
|
||||
ROOT_MOUNT
|
||||
SUBMOUNT
|
||||
}
|
||||
|
||||
type Mount {
|
||||
mountpoint: String!
|
||||
type: MountType!
|
||||
id: Int!
|
||||
taskID: Int!
|
||||
sourceDevice: BlockDevice!
|
||||
rootPath: String
|
||||
options: MountOptions!
|
||||
filesystem: String!
|
||||
label: String
|
||||
uuid: String
|
||||
partitionLabel: String
|
||||
partitionUUID: String
|
||||
deviceNumber: String
|
||||
totalSpace: ByteSize
|
||||
freeSpace: ByteSize
|
||||
usedSpace: ByteSize
|
||||
optionalFields: String
|
||||
propagationFlags: String
|
||||
children: [Mount!]!
|
||||
# FIXME
|
||||
# rawOptions: [RawMountOption]
|
||||
}
|
||||
|
||||
type SmartAttributeFlags {
|
||||
autoKeep: Boolean!
|
||||
eventCount: Boolean!
|
||||
errorRate: Boolean!
|
||||
affectsPerformance: Boolean!
|
||||
updatedOnline: Boolean!
|
||||
indicatesFailure: Boolean!
|
||||
}
|
||||
|
||||
enum SmartAttributeType {
|
||||
PRE_FAIL
|
||||
OLD_AGE
|
||||
}
|
||||
|
||||
enum SmartAttributeUpdateType {
|
||||
ALWAYS
|
||||
OFFLINE
|
||||
}
|
||||
|
||||
enum SmartHealth {
|
||||
HEALTHY
|
||||
DETERIORATING
|
||||
FAILING
|
||||
}
|
||||
|
||||
type SmartAttribute {
|
||||
id: Int!
|
||||
name: String!
|
||||
flags: SmartAttributeFlags!
|
||||
value: Int!
|
||||
rawValue: String!
|
||||
worstValueSeen: Int!
|
||||
failureThreshold: Int!
|
||||
type: SmartAttributeType!
|
||||
failingNow: Boolean!
|
||||
failedBefore: Boolean!
|
||||
updatedWhen: SmartAttributeUpdateType!
|
||||
}
|
||||
|
||||
enum BlockDeviceType {
|
||||
DISK
|
||||
PARTITION
|
||||
LOOP_DEVICE
|
||||
}
|
||||
|
||||
type BlockDevice {
|
||||
name: String!
|
||||
type: BlockDeviceType!
|
||||
path: String!
|
||||
mounts(type: MountType): [Mount!]!
|
||||
# mountpoint: String
|
||||
deviceNumber: String!
|
||||
removable: Boolean!
|
||||
readOnly: Boolean!
|
||||
size: ByteSize!
|
||||
children: [BlockDevice!]!
|
||||
# For tree linearization
|
||||
_treecutterDepth: Int
|
||||
_treecutterSequenceNumber: Int
|
||||
}
|
||||
|
||||
type PhysicalDrive {
|
||||
path: String!
|
||||
interface: String!
|
||||
blockDevice: BlockDevice
|
||||
allBlockDevices(type: BlockDeviceType): [BlockDevice!]!
|
||||
smartAvailable: Boolean!
|
||||
smartEnabled: Boolean
|
||||
smartHealth: SmartHealth
|
||||
smartAttributes: [SmartAttribute!]!
|
||||
model: String
|
||||
modelFamily: String
|
||||
serialNumber: String
|
||||
wwn: String,
|
||||
firmwareVersion: String
|
||||
size: ByteSize
|
||||
rpm: Int
|
||||
logicalSectorSize: ByteSize
|
||||
physicalSectorSize: ByteSize
|
||||
formFactor: String
|
||||
ataVersion: String
|
||||
sataVersion: String
|
||||
}
|
||||
|
||||
type LVMPhysicalVolume {
|
||||
path: String!
|
||||
blockDevice: BlockDevice!
|
||||
volumeGroup: LVMVolumeGroup!
|
||||
format: String!
|
||||
size: ByteSize!
|
||||
freeSpace: ByteSize!
|
||||
duplicate: Boolean!
|
||||
allocatable: Boolean!
|
||||
used: Boolean!
|
||||
exported: Boolean!
|
||||
missing: Boolean!
|
||||
}
|
||||
|
||||
type LVMVolumeGroup {
|
||||
name: String!
|
||||
}
|
||||
|
||||
interface Image {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
thumbnail: String
|
||||
originalSource: String!
|
||||
# The below are only available after the image has been downloaded
|
||||
filesize: ByteSize
|
||||
storagePath: String
|
||||
}
|
||||
|
||||
type InstallationMedium implements Image {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
thumbnail: String
|
||||
originalSource: String!
|
||||
# The below are only available after the image has been downloaded
|
||||
filesize: ByteSize
|
||||
storagePath: String
|
||||
}
|
||||
|
||||
type VMImage implements Image {
|
||||
id: String!
|
||||
name: String!
|
||||
description: String
|
||||
thumbnail: String
|
||||
originalSource: String!
|
||||
# The below are only available after the image has been downloaded
|
||||
filesize: ByteSize
|
||||
storagePath: String
|
||||
}
|
||||
|
||||
type HardwareQuery {
|
||||
drives(paths: [String]): [PhysicalDrive!]!
|
||||
}
|
||||
|
||||
type LVMQuery {
|
||||
physicalVolumes: [LVMPhysicalVolume!]!
|
||||
volumeGroups: [LVMVolumeGroup!]!
|
||||
}
|
||||
|
||||
type ImagesQuery {
|
||||
installationMedia: [InstallationMedium!]!
|
||||
vmImages: [VMImage!]!
|
||||
}
|
||||
|
||||
type ResourcesQuery {
|
||||
blockDevices: [BlockDevice!]!
|
||||
lvm: LVMQuery
|
||||
# TODO: RAID
|
||||
images: ImagesQuery
|
||||
}
|
||||
|
||||
type Query {
|
||||
hardware: HardwareQuery!
|
||||
resources: ResourcesQuery!
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
|
||||
const React = require("react");
|
||||
const ansiHTML = require("ansi-html-community");
|
||||
const htmlentities = require("htmlentities");
|
||||
const util = require("util");
|
||||
|
||||
const Layout = require("./layout.jsx");
|
||||
|
||||
module.exports = {
|
||||
query: {
|
||||
hardware: {
|
||||
drives: {
|
||||
path: true,
|
||||
partitions: {
|
||||
$key: "allBlockDevices",
|
||||
name: true,
|
||||
size: true,
|
||||
|
||||
mounts: {
|
||||
mountpoint: true
|
||||
},
|
||||
|
||||
children: {
|
||||
$recurse: true,
|
||||
$recurseLimit: Infinity, // 3 by default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
template: function StorageDeviceList({data}) {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
return (
|
||||
<Layout title="Debug query">
|
||||
<pre
|
||||
className="debugPrint"
|
||||
dangerouslySetInnerHTML={{ __html: ansiHTML(htmlentities.encode(util.inspect(data, { colors: true, depth: null })))}}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
} else {
|
||||
// FIXME: Proper error, or just prevent the route from being set outside of development mode at all
|
||||
return "This view is only accessible in development mode.";
|
||||
}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue