Boilerplate more or less complete
This commit is contained in:
parent
d9e8ce3bba
commit
7b91dd71dc
9 changed files with 130 additions and 47 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -73,3 +73,4 @@ crashlytics.properties
|
||||||
crashlytics-build.properties
|
crashlytics-build.properties
|
||||||
fabric.properties
|
fabric.properties
|
||||||
|
|
||||||
|
logs/**/*.txt
|
|
@ -4,16 +4,24 @@ part of angel.routes.controllers;
|
||||||
class AuthController extends Controller {
|
class AuthController extends Controller {
|
||||||
@override
|
@override
|
||||||
call(Angel app) async {
|
call(Angel app) async {
|
||||||
|
await super.call(app);
|
||||||
|
|
||||||
app.registerMiddleware("auth", (req, res) async {
|
app.registerMiddleware("auth", (req, res) async {
|
||||||
if (req.session['userId'] == null)
|
if (!loggedIn(req)) throw new AngelHttpException.Forbidden();
|
||||||
throw new AngelHttpException.Forbidden();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool loggedIn(RequestContext req) => req.session["userId"] != null;
|
||||||
|
|
||||||
@Expose("/login", method: "POST")
|
@Expose("/login", method: "POST")
|
||||||
login(RequestContext req) async {
|
login(RequestContext req) async {
|
||||||
// Include log-in logic here...
|
// Include log-in logic here...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Expose("/register", method: "POST")
|
||||||
|
register(RequestContext req, UserService Users) async {
|
||||||
|
// And your registration logic...
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ library angel.routes.controllers;
|
||||||
|
|
||||||
import 'package:angel_auth/angel_auth.dart';
|
import 'package:angel_auth/angel_auth.dart';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import '../../services/user/user.dart';
|
||||||
part 'auth.dart';
|
part 'auth.dart';
|
||||||
|
|
||||||
configureServer(Angel app) async {
|
configureServer(Angel app) async {
|
||||||
|
|
|
@ -15,18 +15,18 @@ configureRoutes(Angel app) async {
|
||||||
|
|
||||||
configureAfter(Angel app) async {
|
configureAfter(Angel app) async {
|
||||||
// 404 handler
|
// 404 handler
|
||||||
app.after.add((RequestContext req, ResponseContext res) async {
|
app.after.add((req, res) async => res
|
||||||
res.willCloseItself = true;
|
..status(404)
|
||||||
res.status(404);
|
..render("404", {"path": req.path}));
|
||||||
res.header('Content-Type', 'text/html');
|
|
||||||
res.underlyingResponse
|
// Default error handler
|
||||||
.write(await app.viewGenerator('404', {'path': req.path}));
|
app.onError(
|
||||||
await res.underlyingResponse.close();
|
(e, req, res) async => res.render("error", {"message": e.message}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configureServer(Angel app) async {
|
configureServer(Angel app) async {
|
||||||
await configureBefore(app);
|
await configureBefore(app);
|
||||||
await configureRoutes(app);
|
await configureRoutes(app);
|
||||||
|
await app.configure(Controllers.configureServer);
|
||||||
await configureAfter(app);
|
await configureAfter(app);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ library angel.services;
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:mongo_dart/mongo_dart.dart';
|
import 'package:mongo_dart/mongo_dart.dart';
|
||||||
|
|
||||||
import 'users/users.dart' as Users;
|
import 'user/user.dart' as User;
|
||||||
|
|
||||||
configureServer(Angel app) async {
|
configureServer(Angel app) async {
|
||||||
Db db = new Db(app.properties["mongo_db"]);
|
Db db = new Db(app.properties["mongo_db"]);
|
||||||
await db.open();
|
await db.open();
|
||||||
|
|
||||||
await app.configure(Users.configureServer(db));
|
await app.configure(User.configureServer(db));
|
||||||
}
|
}
|
||||||
|
|
59
lib/src/services/user/user.dart
Normal file
59
lib/src/services/user/user.dart
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_mongo/angel_mongo.dart';
|
||||||
|
import 'package:crypto/crypto.dart' show sha256;
|
||||||
|
import 'package:mongo_dart/mongo_dart.dart';
|
||||||
|
import 'package:validate/validate.dart';
|
||||||
|
import '../../models/user.dart';
|
||||||
|
export '../../models/user.dart';
|
||||||
|
|
||||||
|
configureServer(Db db) {
|
||||||
|
return (Angel app) async {
|
||||||
|
app.use("/api/users", new UserService(db.collection("users")));
|
||||||
|
|
||||||
|
HookedService service = app.service("api/users");
|
||||||
|
app.container.singleton(service.inner);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages users.
|
||||||
|
///
|
||||||
|
/// Here, we extended the base service class. This allows to only expose
|
||||||
|
/// specific methods, and also allows more freedom over things such as validation.
|
||||||
|
class UserService extends Service {
|
||||||
|
MongoTypedService<User> _inner;
|
||||||
|
|
||||||
|
UserService(DbCollection collection):super() {
|
||||||
|
_inner = new MongoTypedService<User>(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
index([Map params]) {
|
||||||
|
if (params != null && params.containsKey("provider")) {
|
||||||
|
// Nobody needs to see the entire user list except for the server.
|
||||||
|
throw new AngelHttpException.Forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _inner.index(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
create(data, [Map params]) {
|
||||||
|
if (params != null && params.containsKey("provider")) {
|
||||||
|
// Deny creating users to the public - this should be done by the server only.
|
||||||
|
throw new AngelHttpException.Forbidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Validate.isKeyInMap("username", data);
|
||||||
|
Validate.isEmail(data["email"]);
|
||||||
|
data["password"] = sha256.convert(data["password"].codeUnits).toString();
|
||||||
|
} catch(e) {
|
||||||
|
throw new AngelHttpException.BadRequest(message: "User must have a username, e-mail address and password.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _inner.create(data, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
read(id, [Map params]) => _inner.read(id, params);
|
||||||
|
}
|
|
@ -1,32 +0,0 @@
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
|
||||||
import 'package:angel_mongo/angel_mongo.dart';
|
|
||||||
import 'package:crypto/crypto.dart' show sha256;
|
|
||||||
import 'package:mongo_dart/mongo_dart.dart';
|
|
||||||
import 'package:validate/validate.dart';
|
|
||||||
import '../../models/user.dart';
|
|
||||||
|
|
||||||
|
|
||||||
hashPassword(HookedServiceEvent event) {
|
|
||||||
if (event.data.password != null) {
|
|
||||||
event.data.password =
|
|
||||||
sha256.convert(event.data.password.codeUnits).toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configureServer(Db db) {
|
|
||||||
return (Angel app) async {
|
|
||||||
app.use("/api/users", new MongoTypedService<User>(db.collection("users")));
|
|
||||||
|
|
||||||
HookedService service = app.service("api/users");
|
|
||||||
|
|
||||||
// Place your hooks here!
|
|
||||||
|
|
||||||
service.beforeCreated.listen((HookedServiceEvent e) {
|
|
||||||
Validate.isKeyInMap("username", e.data);
|
|
||||||
Validate.isEmail(e.data["email"]);
|
|
||||||
Validate.isPassword(e.data["password"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
service.beforeCreated.listen(hashPassword);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
INFO: 2016-09-20 00:52:14.862305: Server listening at http://127.0.0.1:3000
|
|
||||||
CONFIG: 2016-09-20 00:52:14.864663: Starting server with given configuration: {mongo_db: mongodb://localhost:27017/angel, db: {web: {database: angel, password: password, username: username}, migrate: {database: angel, password: password, username: root}}, host: 127.0.0.1, port: 3000}
|
|
||||||
INFO: 2016-09-20 00:52:20.711300: 200 GET / (69 ms)
|
|
49
views/error.mustache
Normal file
49
views/error.mustache
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{{message}}</title>
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: table;
|
||||||
|
font-weight: 100;
|
||||||
|
font-family: 'Lato', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: center;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="content">
|
||||||
|
<div class="title">{{message}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue