feat: implement some api; add md editor to app; login and signup logic.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import 'package:angel3_auth/angel3_auth.dart';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:angel3_orm/angel3_orm.dart' as orm;
|
||||
import 'package:dde_gesture_manager_api/models.dart';
|
||||
|
||||
Future<void> configureServer(Angel app) async {
|
||||
var auth = AngelAuth<User>(
|
||||
jwtKey: app.configuration['jwt_secret'],
|
||||
allowCookie: false,
|
||||
deserializer: (p) async => (UserQuery()..where!.id.equals(int.parse(p)))
|
||||
.getOne(app.container!.make<orm.QueryExecutor>())
|
||||
.then((value) => value.value),
|
||||
serializer: (p) => p.id ?? '',
|
||||
);
|
||||
await auth.configureServer(app);
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'orm.dart' as orm;
|
||||
import 'jwt.dart' as jwt;
|
||||
import 'redis_cache.dart' as redis_cache;
|
||||
|
||||
Future configureServer(Angel app) async {
|
||||
// Include any plugins you have made here.
|
||||
await app.configure(orm.configureServer);
|
||||
await app.configure(jwt.configureServer);
|
||||
await app.configure(redis_cache.configureServer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:neat_cache/neat_cache.dart';
|
||||
|
||||
Future<void> configureServer(Angel app) async {
|
||||
final _log = Logger('RedisPlugin');
|
||||
|
||||
if (app.container == null) {
|
||||
_log.severe('Angel3 container is null');
|
||||
throw StateError('Angel.container is null. All authentication will fail.');
|
||||
}
|
||||
var appContainer = app.container!;
|
||||
final cache = RedisCache(app.configuration);
|
||||
appContainer.registerSingleton(cache);
|
||||
}
|
||||
|
||||
class RedisCache {
|
||||
late Cache cache;
|
||||
|
||||
RedisCache(Map config) {
|
||||
var redisConfig = config['redis'] as Map? ?? {};
|
||||
|
||||
final cacheProvider = Cache.redisCacheProvider(
|
||||
Uri(
|
||||
scheme: 'redis',
|
||||
host: redisConfig['host'],
|
||||
port: redisConfig['port'],
|
||||
userInfo: redisConfig['password'],
|
||||
),
|
||||
commandTimeLimit: const Duration(seconds: 1),
|
||||
);
|
||||
cache = Cache(cacheProvider).withCodec(utf8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:angel3_serialize/angel3_serialize.dart';
|
||||
|
||||
part 'login_success.g.dart';
|
||||
|
||||
@serializable
|
||||
class _LoginSuccess {
|
||||
@SerializableField(isNullable: false)
|
||||
String? token;
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angel3_migration/angel3_migration.dart';
|
||||
import 'package:angel3_serialize/angel3_serialize.dart';
|
||||
import 'package:angel3_orm/angel3_orm.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/base_model.dart';
|
||||
import 'package:optional/optional.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
part 'user.g.dart';
|
||||
|
||||
@serializable
|
||||
@orm
|
||||
abstract class _User extends BaseModel {
|
||||
@Column(isNullable: false, indexType: IndexType.unique)
|
||||
@SerializableField(isNullable: false)
|
||||
String? get email;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
@Column(isNullable: false, length: 32)
|
||||
@SerializableField(isNullable: true, exclude: true)
|
||||
String? get password;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
String? get token;
|
||||
}
|
||||
String secret(String salt) => base64.encode(Hmac(sha256, salt.codeUnits).convert((password ?? '').codeUnits).bytes);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:angel3_auth/angel3_auth.dart';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:dde_gesture_manager_api/apis.dart';
|
||||
import 'package:dde_gesture_manager_api/models.dart';
|
||||
import 'package:mailer/mailer.dart';
|
||||
import 'package:mailer/smtp_server.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import 'controller_extensions.dart';
|
||||
|
||||
Future configureServer(Angel app) async {
|
||||
app.post(Apis.auth.loginOrSignup, (req, res) async {
|
||||
var userParams = UserSerializer.fromMap(req.bodyAsMap);
|
||||
userParams.password = req.bodyAsMap[UserFields.password];
|
||||
var userQuery = UserQuery();
|
||||
userQuery.where?.email.equals(userParams.email ?? '');
|
||||
var user = await userQuery.getOne(req.queryExecutor);
|
||||
|
||||
if (user.isEmpty) {
|
||||
String accessKey = Uuid().v1();
|
||||
|
||||
await req.cache
|
||||
.withPrefix('sign_up:')[accessKey]
|
||||
.set(json.encode({'email': userParams.email, 'password': userParams.password}), Duration(minutes: 30));
|
||||
var smtpConfig = app.configuration['smtp'];
|
||||
var smtpServer =
|
||||
SmtpServer(smtpConfig['host'], ssl: true, username: smtpConfig['username'], password: smtpConfig['password']);
|
||||
var message = Message()
|
||||
..from = Address(smtpConfig['username'])
|
||||
..recipients.add(userParams.email)
|
||||
..subject = '确认注册'
|
||||
..html = await app.viewGenerator!(
|
||||
'confirm_sign_up.html',
|
||||
{
|
||||
"confirm_url": Uri(
|
||||
scheme: Apis.apiScheme,
|
||||
host: Apis.apiHost,
|
||||
port: Apis.apiPort,
|
||||
path: Apis.auth.confirmSignup(accessKey: accessKey.param),
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
send(message, smtpServer);
|
||||
return res.notFound();
|
||||
} else if (user.value.password != userParams.password) {
|
||||
return res.unauthorized();
|
||||
} else {
|
||||
var angelAuth = req.container!.make<AngelAuth>();
|
||||
await angelAuth.loginById(user.value.id!, req, res);
|
||||
var authToken = req.container!.make<AuthToken>();
|
||||
authToken.payload[UserFields.password] = user.value.secret(app.configuration['password_salt']);
|
||||
var serializedToken = authToken.serialize(angelAuth.hmac);
|
||||
return res.json(LoginSuccess(token: serializedToken));
|
||||
}
|
||||
});
|
||||
|
||||
app.get(Apis.auth.confirmSignup.route, (req, res) async {
|
||||
var accessKey = req.params['accessKey'];
|
||||
var cache = req.cache.withPrefix('sign_up:');
|
||||
var signupInfo = await cache[accessKey].get();
|
||||
if (signupInfo != null && signupInfo is String && signupInfo.isNotEmpty) {
|
||||
var decodedSignupInfo = json.decode(signupInfo);
|
||||
var userQuery = UserQuery();
|
||||
userQuery.values.copyFrom(User(
|
||||
email: decodedSignupInfo[UserFields.email],
|
||||
password: decodedSignupInfo[UserFields.password],
|
||||
));
|
||||
await userQuery.insert(req.queryExecutor);
|
||||
cache[accessKey].purge();
|
||||
return res.render('sign_up_result.html', {'success': true});
|
||||
}
|
||||
return res.render('sign_up_result.html', {'success': false});
|
||||
});
|
||||
}
|
||||
@@ -2,12 +2,24 @@ import 'dart:io';
|
||||
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:angel3_orm/angel3_orm.dart' as orm;
|
||||
import 'package:dde_gesture_manager_api/src/config/plugins/redis_cache.dart';
|
||||
import 'package:neat_cache/neat_cache.dart';
|
||||
|
||||
extension ResponseNoContent on ResponseContext {
|
||||
noContent() {
|
||||
statusCode = HttpStatus.noContent;
|
||||
return close();
|
||||
}
|
||||
|
||||
notFound() {
|
||||
statusCode = HttpStatus.notFound;
|
||||
return close();
|
||||
}
|
||||
|
||||
unauthorized() {
|
||||
statusCode = HttpStatus.unauthorized;
|
||||
return close();
|
||||
}
|
||||
}
|
||||
|
||||
extension QueryWhereId on orm.Query {
|
||||
@@ -19,3 +31,7 @@ extension QueryWhereId on orm.Query {
|
||||
extension QueryExecutor on RequestContext {
|
||||
orm.QueryExecutor get queryExecutor => container!.make<orm.QueryExecutor>();
|
||||
}
|
||||
|
||||
extension RedisExecutor on RequestContext {
|
||||
Cache get cache => container!.make<RedisCache>().cache;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:angel3_auth/angel3_auth.dart';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
|
||||
import 'package:dde_gesture_manager_api/models.dart';
|
||||
|
||||
RequestHandler jwtMiddleware() {
|
||||
return (RequestContext req, ResponseContext res, {bool throwError = true}) async {
|
||||
bool _reject(ResponseContext res) {
|
||||
if (throwError) {
|
||||
res.statusCode = 403;
|
||||
throw AngelHttpException.forbidden();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.container != null) {
|
||||
var reqContainer = req.container!;
|
||||
if (reqContainer.has<User>() || req.method == 'OPTIONS') {
|
||||
return true;
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
User user = await reqContainer.makeAsync<User>();
|
||||
var authToken = req.container!.make<AuthToken>();
|
||||
if (user.secret(req.app!.configuration['password_salt']) != authToken.payload[UserFields.password]) {
|
||||
return _reject(res);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return _reject(res);
|
||||
}
|
||||
} else {
|
||||
return _reject(res);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:dde_gesture_manager_api/models.dart';
|
||||
import 'controller_extensions.dart';
|
||||
|
||||
Future configureServer(Angel app) async {
|
||||
app.get(
|
||||
'/user/int:id',
|
||||
(req, res) async {
|
||||
var user = await (UserQuery()..where?.metadata.contains({"uid": req.params[UserFields.id]}))
|
||||
.getOne(req.queryExecutor);
|
||||
|
||||
return res.json(user.value);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'controllers/user_controllers.dart' as user_controllers;
|
||||
import 'controllers/auth_controllers.dart' as auth_controllers;
|
||||
import 'controllers/system_controllers.dart' as system_controllers;
|
||||
|
||||
/// Put your app routes here!
|
||||
@@ -11,9 +11,17 @@ import 'controllers/system_controllers.dart' as system_controllers;
|
||||
|
||||
AngelConfigurer configureServer(FileSystem fileSystem) {
|
||||
return (Angel app) async {
|
||||
// ParseBody middleware
|
||||
app.fallback((req, res) async {
|
||||
if (req.method == "POST") {
|
||||
await req.parseBody();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Typically, you want to mount controllers first, after any global middleware.
|
||||
await app.configure(system_controllers.configureServerWithFileSystem(fileSystem));
|
||||
await app.configure(user_controllers.configureServer);
|
||||
await app.configure(auth_controllers.configureServer);
|
||||
|
||||
// Throw a 404 if no route matched the request.
|
||||
app.fallback((req, res) => throw AngelHttpException.notFound());
|
||||
|
||||
Reference in New Issue
Block a user