I am currently developing an application using Meteor, ReactJS and React Router (for routing). I have a requirement where a user should be able to upload a zipped web site and the Meteor application needs to show this web site as a part of one of it's pages.
A typical zip file would contain a similar pattern as below,
ZippedFolder
|-- css
| |-- bootstrap.css
| |-- bootstrap.min.css
| +-- style.css
|-- js
| |-- bootstrap.js
| |-- bootstrap.min.js
| +-- script.js
+- index.html
I have set up CollectionFS to store the zip file into the file system and using meteorhacks:npm and several npm packages, I'm able to unzip that file to a known location.
HTMLContent = new FS.Collection("html_content", {
stores: [new FS.Store.FileSystem("html_content"]
});
HTMLContent.on('stored', Meteor.bindEnvironment(function (fileObj) {
let unzip = Meteor.npmRequire('unzip'),
fs = Meteor.npmRequire('fs'),
path = fs.realpathSync(process.cwd() + '/../../../cfs/files/html_content/'),
file = fs.realpathSync(path + '/' + fileObj.copies.html_content.key),
output = path + '/' + fileObj._id;
// TODO: Not the best ways to pick the paths. For demo only.
if (!fs.existsSync(output)) {
fs.mkdirSync(output);
}
fs.createReadStream(file).pipe(unzip.Extract({
path: output
}));
}));
On the React Component I'm uploading the zipped HTML content using the following code snippet.
handleZIPUpload(event) {
FS.Utility.eachFile(event, function(file) {
HTMLContent.insert(file, function(err, fileObj) {
if (err) {
console.log(err);
} else {
console.log(fileObj);
}
});
});
},
render(){
return (
<input id="html_content" type="file" onChange={this.handleZIPUpload}/>
)
}
The upload and unzip works. However, CollectionFS does not serve the HTML or the other contents from the unzipped path.
http://meteorhost/cfs/files/html_content/file_id/index.html
I also tried unzipping the html content to the public folder of the meteor folder structure. However, meteor documentation states that the public folder is meant for static assets. As it maintains an index of the assets in the public folder. So it requires a server restart to update the index.
Is there any other location where I could place the HTML content which can be served as is?
UPDATE I sorted this issue by setting up a nginx virtual directory and uploading the files to that location so that the nginx will serve the HTML content.
Related
I have a html file, being generated by a third party library that I need to serve on a route in a node server.
The html file and the related css files are generated in a folder called public having the following structure.
src -|
| - public -|
| | - css -|
| | | - css-file.css
| | - index.html
| - server.js
The html file refers to the css file as
<link rel="stylesheet" href="css/css-file.css" />
I cannot change this, as the index.html and the related css file is being generated by a third party library.
In the server.js file, I have the following code
app.use(express.static(path.join(__dirname, './public')));
app.get('/path/one',(req, res) => {
res.set({'Content-Type': 'text/html'});
res.sendFile(path.join(__dirname, './public/index.html'))
}
app.get('/pathTwo', (req, res) => {
res.set({'Content-Type': 'text/html'});
res.sendFile(path.join(__dirname, './public/index.html'))
}
The css file gets picked up on /pathTwo, but does not get picked up on /path/one. I am not able to figure out why.
Edit
One thing that I notice from the logs
For /path/one, node is looking for the file at the location /path/css/css-file.css and not at /css/css-file.css
From express docs for express.static
The function determines the file to serve by combining req.url with the provided root directory.
Thanks in advance for your help
WORKAROUND
Found a workaround for this.
Looking at the logs and the network calls, for the css, the following call is being made for fetching the css file
GET /path/css/css-file.css
This call fails with a 404, resulting in the css not being used for the html.
I added a new route in the server, as below
app.get(`path/css/:fileName`, (req, res) => {
const fileName = req.params.fileName;
const filePath = path.join(__dirname, `./public/css/${fileName}`);
res.set({'Content-Type', 'test/css'})
res.sendFile(filePath);
}
This returns the CSS file from the desired path, and ultimately being used by the HTML file.
This seems to be just a workaround, and not a solution from within express.static.
Hope that someone would provide a solution here.
Thanks in advance!!
Problem
I am trying to write a Single Page Application (SPA) where initially the app shows module "A". When the user clicks an element in "A", module "B" is displayed and is passed an ID from A. (For example A displays a list of Employee IDs, clicking on one employee means B will display details of that employee)
Initially my URL is :
http://localhost:8000/
Clicking on an item in A with an id of 123, the URL changes to following which is correct:
http://localhost:8000/A/123
However, I get the following error
GET http://localhost:8000/b/js/viewModels/B.js net::ERR_ABORTED 404 (Not Found)
ojModule failed to load viewModels/B
ojlogger.js:257 Error: Script error for "viewModels/B"
I do not know why it has changed the path and added an extra "/b/" to get the B.js/B.html file. Of course it can not find this file as there is no such folder "b" in my project structure.
Oracle Jet Cookbook Sample
https://www.oracle.com/webfolder/technetwork/jet/jetCookbook.html?component=router&demo=stateParams
I am using the sample in the OracleJet Cookbook for a Router with State Parameters. If you open this example in full screen you see that the URL for the first screen (A) is
https://www.oracle.com/webfolder/technetwork/jet/content/router-stateParams/demo.html
Clicking on a person in the list changes the URL to the following, which is the same as mine.
https://www.oracle.com/webfolder/technetwork/jet/content/router-stateParams/demo.html/detail/7566
This cookbook sample does not error like mine.
My Code
project structure
src
|- index.html
|- js
|- main.js
|- viewModels
|- A.js
|- B.js
|- views
|- A.html
|- B.html
index.html
....
<body>
<div id="routing-container">
<div data-bind="ojModule:router.moduleConfig"></div>
</div>
</body>
....
main.js
requirejs.config(
{
baseUrl: 'js',
....
}
require(['ojs/ojbootstrap', 'ojs/ojrouter', 'knockout', 'ojs/ojmodule-element-utils', 'ojs/ojknockout', 'ojs/ojmodule'],
function (Bootstrap, Router, ko) {
// Retrieve the router static instance and configure the states
var router = Router.rootInstance;
router.configure({
'a': {label: 'a', value: 'A', isDefault: true},
'b/{id}': {label: 'b', value: 'B' }
});
var viewModel = {
router: router
};
Bootstrap.whenDocumentReady().then(
function(){
ko.applyBindings(viewModel, document.getElementById('routing-container'));
Router.sync();
}
);
});
A.html
....
<div on-click="[[onClicked]]" >
....
</div>
...
A.js
define(['ojs/ojcore', 'ojs/ojrouter', 'knockout', '],
function (oj, Router, ko) {
function AViewModel(params) {
....
router = Router.rootInstance;
....
this.onClicked= function(event) {
router.go('b/'+ 123);
}
....
};
}
return AViewModel;
}
Attempts
I have tried adding one of the following in "main.js" and it doesn't make a difference.
Router.defaults['baseUrl'] = '';
Router.defaults['baseUrl'] = 'js';
Router.defaults['baseUrl'] = '/js';
Router.defaults['baseUrl'] = '/js/';
Updated answer after gleeming more information
I have finally resolved this with some advice from a colleague.
Make sure the "baseUrl" specified in your requireJS is absolute when you are using the urlPathAdapter router.
main.js
requirejs.config(
{
baseUrl: '/js',
RequireJS's baseUrl is used as the starting location for RequireJS to download its relative content. Most of the time its set to "js" but that will not work nicely with the UrlPathAdapter router. This is because the router will change the URL which RequireJS tries to add its path to when its not absolute. The result is that the path requireJS is trying to use to get its content is invalid.
For example:
you accessed your app with the URL "protocol://server:port/my/app"
RequireJS will try and access content by appending "js", for example "protocol://server:port/my/app/js/viewModel/..." which works when you are at the root of your application
you use the router to navigate to a different url, the url is now "protocol://server:port/my/app/newPath"
now RequireJS will try and use the URL "protocol://server:port/my/app/newPath/js/viewModel" which is wrong
When the RequireJS baseURL is absolute, it will always be added to the apps URL, for example "protocol://server:port/my/app/js/viewModel" where the content will be found
NOTE: I also ensured the baseUrl in "path-mapping" was absolute as well
path_mapping.json
{
"baseUrl": "/js",
Another solution was to change my router adapter from the default urlPathAdapter to urlParamAdapter.
Router.defaults.urlAdapter = new Router.urlParamAdapter();
var router = Router.rootInstance;
I started out with this code, app.js:
var app = angular.module('myProject', ['ngStorage', 'ngRoute'])
.config(function ($routeProvider) {
$routeProvider.when("/", {
controller: "loginController",
controllerAs: "vm",
templateUrl: "/index.html"
}).otherwise({
controller: "loginController",
controllerAs: "vm",
templateUrl: "/views/login.html"
});
});
If someone visited -
http://localhost:8885
it would redirect to http://localhost:8885/#!/ and show the index.html template the $routeProvider had declared.
And if someone visited either -
http://localhost:8885/#!/asdfasdfasdf or http://localhost:8885/#!/1234
the pages would load and show the /views/login.html template.
Everything was working fine.
Then, to exclude the hash-bang, known as (/#!/), I added:
$locationProvider.html5Mode(true) to the end of the config mentioned above.
This caused my project to no longer serve my src -> MyProject -> wwwroot folder files anymore, but rather the content inside of my src -> Views -> App -> Index.html file. File structure shown below:
Why is this the case? How do I solve this to not only exclude the hash-bang but also to serve from my wwwroot folder?
EXTRA INFO -
My _Layout.cshtml file has this for a body:
<body ng-view>
<div>
#RenderBody()
</div>
</body>
I've also tried to place <base href="/"> inside the <head> but it didn't work.
Any help?
I have created a custom module and added requires-config.js (app\code\Namespace\Modulename\view\frontend).
var config = {
map: {
'*': {
test: 'https://example.com/test.js'
}
}
};
After that I have deploy the static content, now I can see the external js has added in following location (pub\static_requirejs\frontend\Magento\luma\en_US),but still external js is not loaded in page source (using console).
Still I need to add the js files in layout?
<head>
<link src="https://example.com/test.js"/>
</head>
Its good practise to put your js in Modulename/view/frontend/web/js folder rather than loading from url.
var config = {
"map": {
"*": {
"test": "Modulename/js/test"
}
}
};
Also you don't need to add .js extenstion in the require js file.
then check it will load
Please clear the cache and also do static content deploy
I want to serve a static HTML file from MeteorJS's public folder (as is possible with Rails and Express). The reason I'm doing this is because I have one template for the dynamic "admin" part of my webapp and another for the sales-y "frontend" part of the app.
I don't want this file to be wrapped in a Meteor template as suggested in this answer as it will automatically bring in the minified CSS, etc... that the dynamic pages use.
Is there a way I can setup the public folder (and all its subfolders) so that it serves index.html? This way http://app.com/ will load public/index.html?
You could use the private folder instead and then use Assets.getText to load the contents of the file, then serve it with a server-side router from iron-router.
So off the top of my head the code would look something like this:
if (Meteor.isServer) {
Router.map(function() {
this.route('serverRoute', {
path: '/',
where: 'server',
action: function() {
var contents = Assets.getText('index.html');
this.response.end(contents);
}
});
});
}
this is what I put in bootstrap.js
Router.route('/', {
where: 'server'
}).get(function() {
var contents;
contents = Assets.getText('index.html');
return this.response.end(contents);
});