1.3.1
This commit is contained in:
parent
16c42d14f9
commit
0c18eefcca
5 changed files with 100 additions and 8 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
# 1.3.1
|
||||||
|
* Added an `accepts` option to `pushState`.
|
||||||
|
* Added optional directory listings.
|
||||||
|
|
||||||
# 1.3.0-alpha+1
|
# 1.3.0-alpha+1
|
||||||
* ETags once again only encode the first 50 bytes of files. Resolves [#27](https://github.com/angel-dart/static/issues/27).
|
* ETags once again only encode the first 50 bytes of files. Resolves [#27](https://github.com/angel-dart/static/issues/27).
|
||||||
|
|
||||||
|
|
18
example/main.dart
Normal file
18
example/main.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_static/angel_static.dart';
|
||||||
|
import 'package:file/local.dart';
|
||||||
|
|
||||||
|
main() async {
|
||||||
|
var app = new Angel();
|
||||||
|
var fs = const LocalFileSystem();
|
||||||
|
var vDir = new VirtualDirectory(
|
||||||
|
app,
|
||||||
|
fs,
|
||||||
|
allowDirectoryListing: true,
|
||||||
|
source: fs.directory(fs.currentDirectory),
|
||||||
|
);
|
||||||
|
app.use(vDir.handleRequest);
|
||||||
|
|
||||||
|
var server = await app.startServer('127.0.0.1', 3000);
|
||||||
|
print('Listening at http://${server.address.address}:${server.port}');
|
||||||
|
}
|
|
@ -60,13 +60,15 @@ class CachingVirtualDirectory extends VirtualDirectory {
|
||||||
this.noCache: false,
|
this.noCache: false,
|
||||||
this.onlyInProduction: false,
|
this.onlyInProduction: false,
|
||||||
this.useEtags: true,
|
this.useEtags: true,
|
||||||
|
bool allowDirectoryListing,
|
||||||
String publicPath,
|
String publicPath,
|
||||||
callback(File file, RequestContext req, ResponseContext res)})
|
callback(File file, RequestContext req, ResponseContext res)})
|
||||||
: super(app, fileSystem,
|
: super(app, fileSystem,
|
||||||
source: source,
|
source: source,
|
||||||
indexFileNames: indexFileNames ?? ['index.html'],
|
indexFileNames: indexFileNames ?? ['index.html'],
|
||||||
publicPath: publicPath ?? '/',
|
publicPath: publicPath ?? '/',
|
||||||
callback: callback);
|
callback: callback,
|
||||||
|
allowDirectoryListing: allowDirectoryListing);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> serveFile(
|
Future<bool> serveFile(
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:mime/mime.dart';
|
import 'package:mime/mime.dart';
|
||||||
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
||||||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
||||||
|
@ -42,11 +43,15 @@ class VirtualDirectory {
|
||||||
/// An optional public path to map requests to.
|
/// An optional public path to map requests to.
|
||||||
final String publicPath;
|
final String publicPath;
|
||||||
|
|
||||||
|
/// If `true` (default: `false`), then if a directory does not contain any of the specific [indexFileNames], a default directory listing will be served.
|
||||||
|
final bool allowDirectoryListing;
|
||||||
|
|
||||||
VirtualDirectory(this.app, this.fileSystem,
|
VirtualDirectory(this.app, this.fileSystem,
|
||||||
{Directory source,
|
{Directory source,
|
||||||
this.indexFileNames: const ['index.html'],
|
this.indexFileNames: const ['index.html'],
|
||||||
this.publicPath: '/',
|
this.publicPath: '/',
|
||||||
this.callback}) {
|
this.callback,
|
||||||
|
this.allowDirectoryListing: false}) {
|
||||||
_prefix = publicPath.replaceAll(_straySlashes, '');
|
_prefix = publicPath.replaceAll(_straySlashes, '');
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
_source = source;
|
_source = source;
|
||||||
|
@ -68,13 +73,22 @@ class VirtualDirectory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handler that serves the file at the given path, unless the user has requested that path.
|
/// A handler that serves the file at the given path, unless the user has requested that path.
|
||||||
RequestMiddleware pushState(String path) {
|
///
|
||||||
|
/// You can also limit this functionality to specific values of the `Accept` header, ex. `text/html`.
|
||||||
|
/// If [accepts] is `null`, OR at least one of the content types in [accepts] is present,
|
||||||
|
/// the view will be served.
|
||||||
|
RequestMiddleware pushState(String path, {Iterable accepts}) {
|
||||||
var vPath = path.replaceAll(_straySlashes, '');
|
var vPath = path.replaceAll(_straySlashes, '');
|
||||||
if (_prefix?.isNotEmpty == true) vPath = '$_prefix/$vPath';
|
if (_prefix?.isNotEmpty == true) vPath = '$_prefix/$vPath';
|
||||||
|
|
||||||
return (RequestContext req, ResponseContext res) {
|
return (RequestContext req, ResponseContext res) {
|
||||||
var path = req.path.replaceAll(_straySlashes, '');
|
var path = req.path.replaceAll(_straySlashes, '');
|
||||||
if (path == vPath) return new Future<bool>.value(true);
|
if (path == vPath) return new Future<bool>.value(true);
|
||||||
|
|
||||||
|
if (accepts?.isNotEmpty == true) {
|
||||||
|
if (!accepts.any(req.accepts)) return new Future<bool>.value(true);
|
||||||
|
}
|
||||||
|
|
||||||
return servePath(vPath, req, res);
|
return servePath(vPath, req, res);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -93,15 +107,15 @@ class VirtualDirectory {
|
||||||
|
|
||||||
var absolute = source.absolute.uri.resolve(path).toFilePath();
|
var absolute = source.absolute.uri.resolve(path).toFilePath();
|
||||||
var stat = await fileSystem.stat(absolute);
|
var stat = await fileSystem.stat(absolute);
|
||||||
return await serveStat(absolute, stat, req, res);
|
return await serveStat(absolute, path, stat, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the file at the path given by the [stat] to a response.
|
/// Writes the file at the path given by the [stat] to a response.
|
||||||
Future<bool> serveStat(String absolute, FileStat stat, RequestContext req,
|
Future<bool> serveStat(String absolute, String relative, FileStat stat, RequestContext req,
|
||||||
ResponseContext res) async {
|
ResponseContext res) async {
|
||||||
if (stat.type == FileSystemEntityType.DIRECTORY)
|
if (stat.type == FileSystemEntityType.DIRECTORY)
|
||||||
return await serveDirectory(
|
return await serveDirectory(
|
||||||
fileSystem.directory(absolute), stat, req, res);
|
fileSystem.directory(absolute), relative, stat, req, res);
|
||||||
else if (stat.type == FileSystemEntityType.FILE)
|
else if (stat.type == FileSystemEntityType.FILE)
|
||||||
return await serveFile(fileSystem.file(absolute), stat, req, res);
|
return await serveFile(fileSystem.file(absolute), stat, req, res);
|
||||||
else if (stat.type == FileSystemEntityType.LINK) {
|
else if (stat.type == FileSystemEntityType.LINK) {
|
||||||
|
@ -112,7 +126,7 @@ class VirtualDirectory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serves the index file of a [directory], if it exists.
|
/// Serves the index file of a [directory], if it exists.
|
||||||
Future<bool> serveDirectory(Directory directory, FileStat stat,
|
Future<bool> serveDirectory(Directory directory, String relative, FileStat stat,
|
||||||
RequestContext req, ResponseContext res) async {
|
RequestContext req, ResponseContext res) async {
|
||||||
for (String indexFileName in indexFileNames) {
|
for (String indexFileName in indexFileNames) {
|
||||||
final index =
|
final index =
|
||||||
|
@ -122,6 +136,60 @@ class VirtualDirectory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowDirectoryListing == true) {
|
||||||
|
res.headers['content-type'] = 'text/html';
|
||||||
|
res
|
||||||
|
..write('<!DOCTYPE html>')
|
||||||
|
..write('<html>')
|
||||||
|
..write(
|
||||||
|
'<head><meta name="viewport" content="width=device-width,initial-scale=1">')
|
||||||
|
..write('<style>ul { list-style-type: none; }</style>')
|
||||||
|
..write('</head></html><body>');
|
||||||
|
|
||||||
|
res.write('<li><a href="..">..</a></li>');
|
||||||
|
|
||||||
|
List<FileSystemEntity> entities = await directory
|
||||||
|
.list(followLinks: false)
|
||||||
|
.toList()
|
||||||
|
.then((l) => new List.from(l));
|
||||||
|
entities.sort((a, b) {
|
||||||
|
if (a is Directory) {
|
||||||
|
if (b is Directory) return a.path.compareTo(b.path);
|
||||||
|
return -1;
|
||||||
|
} else if (a is File) {
|
||||||
|
if (b is Directory)
|
||||||
|
return 1;
|
||||||
|
else if (b is File) return a.path.compareTo(b.path);
|
||||||
|
return -1;
|
||||||
|
} else if (b is Link) return a.path.compareTo(b.path);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var entity in entities) {
|
||||||
|
var stub = p.basename(entity.path);
|
||||||
|
var href = stub;
|
||||||
|
String type;
|
||||||
|
|
||||||
|
if (entity is File)
|
||||||
|
type = '[File]';
|
||||||
|
else if (entity is Directory)
|
||||||
|
type = '[Directory]';
|
||||||
|
else if (entity is Link) type = '[Link]';
|
||||||
|
|
||||||
|
if (relative.isNotEmpty)
|
||||||
|
href = '/' + relative + '/' + stub;
|
||||||
|
|
||||||
|
if (entity is Directory)
|
||||||
|
href += '/';
|
||||||
|
|
||||||
|
res.write('<li><a href="$href">$type $stub</a></li>');
|
||||||
|
}
|
||||||
|
|
||||||
|
res..write('</body></html>');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ environment:
|
||||||
sdk: ">=1.19.0"
|
sdk: ">=1.19.0"
|
||||||
homepage: https://github.com/angel-dart/static
|
homepage: https://github.com/angel-dart/static
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
version: 1.3.0-alpha+1
|
version: 1.3.1
|
||||||
dependencies:
|
dependencies:
|
||||||
angel_framework: ^1.1.0-alpha
|
angel_framework: ^1.1.0-alpha
|
||||||
file: ^2.0.0
|
file: ^2.0.0
|
||||||
|
|
Loading…
Reference in a new issue